@techsee/techsee-media-service 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -0
  2. package/lib/constants.js +20 -0
  3. package/lib/constants.js.map +1 -0
  4. package/lib/event-emitter.js +39 -0
  5. package/lib/event-emitter.js.map +1 -0
  6. package/lib/index.js +64 -0
  7. package/lib/index.js.map +1 -0
  8. package/lib/opentok/session.js +304 -0
  9. package/lib/opentok/session.js.map +1 -0
  10. package/lib/opentok/stream.js +310 -0
  11. package/lib/opentok/stream.js.map +1 -0
  12. package/lib/publisher.js +143 -0
  13. package/lib/publisher.js.map +1 -0
  14. package/lib/qos/raw-qos.js +319 -0
  15. package/lib/qos/raw-qos.js.map +1 -0
  16. package/lib/service.js +316 -0
  17. package/lib/service.js.map +1 -0
  18. package/lib/session.js +104 -0
  19. package/lib/session.js.map +1 -0
  20. package/lib/stream.js +195 -0
  21. package/lib/stream.js.map +1 -0
  22. package/lib/subscriber.js +90 -0
  23. package/lib/subscriber.js.map +1 -0
  24. package/lib/webrtc/constants.js +112 -0
  25. package/lib/webrtc/constants.js.map +1 -0
  26. package/lib/webrtc/helper.js +169 -0
  27. package/lib/webrtc/helper.js.map +1 -0
  28. package/lib/webrtc/session-kms.js +558 -0
  29. package/lib/webrtc/session-kms.js.map +1 -0
  30. package/lib/webrtc/session-loopback.js +340 -0
  31. package/lib/webrtc/session-loopback.js.map +1 -0
  32. package/lib/webrtc/session-turn.js +807 -0
  33. package/lib/webrtc/session-turn.js.map +1 -0
  34. package/lib/webrtc/session-turn.v2.js +2 -0
  35. package/lib/webrtc/session-turn.v2.js.map +1 -0
  36. package/lib/webrtc/session.js +40 -0
  37. package/lib/webrtc/session.js.map +1 -0
  38. package/lib/webrtc/stream.js +631 -0
  39. package/lib/webrtc/stream.js.map +1 -0
  40. package/lib/webrtc/temasys/adapter-loader.js +12 -0
  41. package/lib/webrtc/temasys/adapter-loader.js.map +1 -0
  42. package/lib/webrtc/temasys/adapter.js +5861 -0
  43. package/lib/webrtc/temasys/adapter.js.map +1 -0
  44. package/package.json +53 -0
@@ -0,0 +1,631 @@
1
+ 'use strict';
2
+
3
+ exports.__esModule = true;
4
+ exports.TsWebRTCStream = exports.EVENTS = undefined;
5
+
6
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
7
+
8
+ var _stream = require('../stream');
9
+
10
+ var _session = require('../session');
11
+
12
+ var _subscriber = require('../subscriber');
13
+
14
+ var _constants = require('./constants');
15
+
16
+ var _tsEnvironmentDetect = require('techsee-common/lib/helpers/ts-environment-detect');
17
+
18
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
19
+
20
+ function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
21
+
22
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
23
+
24
+ exports.EVENTS = _stream.EVENTS;
25
+
26
+
27
+ var TORCH_DELAY = 1500;
28
+ var isIE11 = new _tsEnvironmentDetect.TsEnvironmentDetect().isIE11();
29
+
30
+ var AdapterJS = void 0;
31
+
32
+ if (isIE11) {
33
+ AdapterJS = require('./temasys/adapter-loader')(); // eslint-disable-line global-require
34
+ }
35
+
36
+ /**
37
+ * WebRTC-specific implementation of TsMediaStream.
38
+ *
39
+ * Instances of this class are mostly used internally by TsMediaPublisher
40
+ * and for subscribers creating.
41
+ *
42
+ * @class
43
+ * @extends TsMediaStream
44
+ *
45
+ * @see TsMediaStream
46
+ */
47
+
48
+ var TsWebRTCStream = exports.TsWebRTCStream = function (_TsMediaStream) {
49
+ _inherits(TsWebRTCStream, _TsMediaStream);
50
+
51
+ function TsWebRTCStream(session, mediaStream, name, isRemote, videoTrackSettings) {
52
+ _classCallCheck(this, TsWebRTCStream);
53
+
54
+ var _this = _possibleConstructorReturn(this, _TsMediaStream.call(this, mediaStream.id, session, isRemote, videoTrackSettings));
55
+
56
+ _this._torchDelayTimeOut = null;
57
+ _this._torchOffTimeOut = null;
58
+
59
+ /**
60
+ * Browser's native media stream
61
+ *
62
+ * @private
63
+ * @member { Object }
64
+ *
65
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaStream MediaStream API}
66
+ */
67
+ _this._mediaStream = mediaStream;
68
+
69
+ /**
70
+ * Instance of TsWebRTCSession
71
+ *
72
+ * @private
73
+ * @member { Object }
74
+ *
75
+ * @see TsWebRTCSession
76
+ */
77
+ _this._session = session;
78
+
79
+ _this._name = name;
80
+
81
+ if (!_this._name) {
82
+ if (_this.hasVideo) {
83
+ if (!_this._session._isMobile) {
84
+ _this._name = _constants.DEFAULT_STREAM_NAMES.MOBILE_CLIENT_VIDEO_STREAM;
85
+ }
86
+ } else if (_this.hasAudio) {
87
+ if (_this._session._isMobile) {
88
+ _this._name = _this.remote ? _constants.DEFAULT_STREAM_NAMES.DASHBOARD_AUDIO_STREAM : _constants.DEFAULT_STREAM_NAMES.MOBILE_CLIENT_AUDIO_STREAM;
89
+ } else {
90
+ _this._name = _this.remote ? _constants.DEFAULT_STREAM_NAMES.MOBILE_CLIENT_AUDIO_STREAM : _constants.DEFAULT_STREAM_NAMES.DASHBOARD_AUDIO_STREAM;
91
+ }
92
+ }
93
+ }
94
+
95
+ if (!_this._name) {
96
+ _this._name = Math.random().toString();
97
+ }
98
+
99
+ // TODO I think this will never be emitted, as both app/mobile call session.off() before they call session.disconnect(). And we call the destroy elsewhere
100
+ // TODO Need to make sure that it's always the case, especially for IOS
101
+ session.on(_session.EVENTS.DISCONNECTED, function () {
102
+ if (_this.publisher) {
103
+ _this.publisher.destroy();
104
+ }
105
+
106
+ if (_this.subscriber) {
107
+ _this.subscriber.destroy();
108
+ }
109
+ });
110
+ return _this;
111
+ }
112
+
113
+ /**
114
+ * Unique id of the stream
115
+ *
116
+ * @member { String }
117
+ * @type { String }
118
+ */
119
+
120
+
121
+ TsWebRTCStream.prototype._getPeerConnection = function _getPeerConnection() {
122
+ // KMS store the peer connection as _pc, TURN store it as _lastPc
123
+ return _.get(this._session, '_lastPc.peerConnection') || _.get(this._session, '_pc.peerConnection');
124
+ };
125
+
126
+ /**
127
+ * Publish or unpublish audio tracks.
128
+ *
129
+ * Used internally by TsMediaPublisher.publishAudio
130
+ *
131
+ * @method
132
+ *
133
+ * @param { Boolean } publish
134
+ *
135
+ * @fires SESSION_EVENTS.STREAM_PROPERTY_CHANGED
136
+ * @fires STREAM_EVENTS.PROPERTY_CHANGED
137
+ */
138
+
139
+
140
+ TsWebRTCStream.prototype.publishAudio = function publishAudio(publish) {
141
+ this._mediaStream.getTracks().forEach(function (track) {
142
+ if (track.kind === 'audio') {
143
+ if (track.setEnabled) {
144
+ track.setEnabled(publish);
145
+ } else {
146
+ track.enabled = publish;
147
+ }
148
+ }
149
+ });
150
+
151
+ this._session.emit(_session.EVENTS.STREAM_PROPERTY_CHANGED, {
152
+ stream: this,
153
+ property: 'hasAudio',
154
+ value: publish
155
+ });
156
+
157
+ this.emit(_stream.EVENTS.PROPERTY_CHANGED, {
158
+ stream: this,
159
+ property: 'hasAudio',
160
+ value: publish
161
+ });
162
+ };
163
+
164
+ /**
165
+ * Publish or unpublish video tracks.
166
+ *
167
+ * Used internally by TsMediaPublisher.publishVideo
168
+ *
169
+ * @method
170
+ *
171
+ * @param { Boolean } publish
172
+ *
173
+ * @fires SESSION_EVENTS.STREAM_PROPERTY_CHANGED
174
+ */
175
+
176
+
177
+ TsWebRTCStream.prototype.publishVideo = function publishVideo(publish) {
178
+ this._mediaStream.getTracks().forEach(function (track) {
179
+ if (track.kind === 'video') {
180
+ if (track.setEnabled) {
181
+ track.setEnabled(publish);
182
+ } else {
183
+ track.enabled = publish;
184
+ }
185
+ }
186
+ });
187
+
188
+ this._session.emit(_session.EVENTS.STREAM_PROPERTY_CHANGED, {
189
+ stream: this,
190
+ property: 'hasVideo',
191
+ value: publish
192
+ });
193
+ };
194
+
195
+ /**
196
+ * Subscribe to a remote stream.
197
+ *
198
+ * Used internally by TsMediaStream.subscribe
199
+ *
200
+ * @method
201
+ *
202
+ * @param { TsMediaStream } stream
203
+ * @param { DOMElement } target
204
+ * @param { Object } params
205
+ * @param { String|Number } params.height Subscriber's view height
206
+ * @param { String|Number } params.width Subscriber's view width
207
+ * @param { String } params.fitMode Subscriber's view fit mode
208
+ * @param { String } params.insertMode Subscriber's view insert mode (Is not in use yet)
209
+ *
210
+ * @returns { Promise<TsMediaSubscriber> } subsriber instance
211
+ *
212
+ * @see TsMediaStream
213
+ */
214
+
215
+
216
+ TsWebRTCStream.prototype.subscribe = function subscribe(target) {
217
+ var _this2 = this;
218
+
219
+ var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
220
+ height: '100%',
221
+ width: '100%',
222
+ fitMode: 'contain',
223
+ insertMode: 'append'
224
+ };
225
+
226
+ if (!params) {
227
+ params = target;
228
+ target = null;
229
+ }
230
+
231
+ return Promise.resolve().then(function () {
232
+ //
233
+ // New video element where publishing video stream will be rendered
234
+ //
235
+ var video = document.createElement('video');
236
+
237
+ video.muted = true;
238
+ video.playsInline = true;
239
+
240
+ var attachedVideo = void 0;
241
+
242
+ if (!isIE11) {
243
+ video.srcObject = _this2._mediaStream;
244
+
245
+ // Hack to override behavior introduced by Chrome/FF that unmuted video elements can't auto play
246
+ video.setAttribute('muted', '');
247
+
248
+ video.play();
249
+ } else {
250
+ target.appendChild(video);
251
+
252
+ attachedVideo = AdapterJS.attachMediaStream(video, _this2._mediaStream);
253
+
254
+ if (attachedVideo && attachedVideo.play) {
255
+ attachedVideo.play();
256
+ }
257
+ }
258
+
259
+ //
260
+ // If height and width are numbers then add 'px' ending.
261
+ // Else use it as is (suppose that it's already 'px' or '%')
262
+ //
263
+ var elementHeight = typeof params.height === 'string' ? params.height : params.height + 'px';
264
+ var elementWidth = typeof params.width === 'string' ? params.width : params.width + 'px';
265
+
266
+ _.assign((isIE11 ? attachedVideo : video).style, {
267
+ height: elementHeight,
268
+ width: elementWidth,
269
+
270
+ 'object-fit': params.fitMode
271
+ });
272
+
273
+ if (!isIE11) {
274
+ target.appendChild(video);
275
+ }
276
+
277
+ var subscriber = new _subscriber.TsMediaSubscriber({
278
+ target: target,
279
+ _session: _this2._session,
280
+ stream: _this2,
281
+ _element: isIE11 ? attachedVideo : video
282
+ });
283
+
284
+ _this2.setupDimensionsListener(isIE11 ? attachedVideo : video, subscriber);
285
+
286
+ _this2.subscriber = subscriber;
287
+
288
+ return subscriber;
289
+ });
290
+ };
291
+
292
+ /**
293
+ * Unsubscribe from the stream and destroy subscriber's view.
294
+ *
295
+ * Used internally by TsMediaSubscriber.destroy.
296
+ *
297
+ * @method
298
+ *
299
+ * @param { TsMediaSubscriber } subscriber
300
+ *
301
+ * @returns { Promise }
302
+ *
303
+ * @fires SUBSCRIBER_EVENTS#DESTROYED
304
+ *
305
+ * @see TsMediaSubscriber#destroy
306
+ */
307
+
308
+
309
+ TsWebRTCStream.prototype.destroySubscriber = function destroySubscriber(subscriber) {
310
+ return Promise.resolve().then(function () {
311
+ if (!isIE11) {
312
+ subscriber._element.remove();
313
+ } else {
314
+ subscriber._element.parentNode.removeChild(subscriber._element);
315
+ }
316
+
317
+ subscriber.emit(_subscriber.EVENTS.DESTROYED);
318
+ });
319
+ };
320
+
321
+ /* eslint-disable no-unused-vars */
322
+
323
+
324
+ TsWebRTCStream.prototype.getSnapshot = function getSnapshot(tsMediaElement) {
325
+ var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
326
+
327
+ var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
328
+ _ref$format = _ref.format,
329
+ format = _ref$format === undefined ? 'image/png' : _ref$format,
330
+ _ref$quality = _ref.quality,
331
+ quality = _ref$quality === undefined ? 0.92 : _ref$quality,
332
+ x = _ref.x,
333
+ y = _ref.y,
334
+ w = _ref.w,
335
+ h = _ref.h;
336
+
337
+ var videoElement = tsMediaElement._element;
338
+ var videoWidth = videoElement.videoWidth,
339
+ videoHeight = videoElement.videoHeight;
340
+
341
+ var crop = !_.isUndefined(x) && !_.isUndefined(y) && !_.isUndefined(w) && !_.isUndefined(h);
342
+ var resize = !crop && (!_.isUndefined(w) || !_.isUndefined(h));
343
+
344
+ if (resize) {
345
+ if (!h) {
346
+ h = w * (videoHeight / videoWidth);
347
+ } else if (!w) {
348
+ w = h * (videoWidth / videoHeight);
349
+ }
350
+ }
351
+
352
+ function getImgData() {
353
+ var canvas = document.createElement('canvas');
354
+ var ctx = canvas.getContext('2d');
355
+
356
+ if (crop) {
357
+ canvas.width = w;
358
+ canvas.height = h;
359
+
360
+ ctx.drawImage(videoElement, x, y, w, h, 0, 0, w, h);
361
+ } else if (resize) {
362
+ canvas.width = w;
363
+ canvas.height = h;
364
+
365
+ ctx.drawImage(videoElement, 0, 0, w, h);
366
+ } else {
367
+ canvas.height = videoHeight;
368
+ canvas.width = videoWidth;
369
+
370
+ ctx.drawImage(videoElement, 0, 0);
371
+ }
372
+
373
+ return canvas.toDataURL(format, quality);
374
+ }
375
+
376
+ function getImgDataIE11() {
377
+ return new Promise(function (resolve) {
378
+ var canvas = document.createElement('canvas');
379
+ var ctx = canvas.getContext('2d');
380
+
381
+ if (crop || resize) {
382
+ canvas.width = w;
383
+ canvas.height = h;
384
+ } else {
385
+ canvas.height = videoHeight;
386
+ canvas.width = videoWidth;
387
+ }
388
+
389
+ function _drawAndRemove(image) {
390
+ if (crop) {
391
+ ctx.drawImage(image, x, y, w, h, 0, 0, w, h);
392
+ } else if (resize) {
393
+ ctx.drawImage(image, 0, 0, w, h);
394
+ } else {
395
+ ctx.drawImage(image, 0, 0, videoWidth, videoHeight);
396
+ }
397
+
398
+ return canvas.toDataURL(format, quality);
399
+ }
400
+
401
+ if (!_.get(AdapterJS, 'WebRTCPlugin.plugin.valid')) {
402
+ return resolve(_drawAndRemove(videoElement));
403
+ }
404
+
405
+ var base64 = videoElement.getFrame();
406
+ var image = new Image();
407
+
408
+ image.onload = function () {
409
+ return resolve(_drawAndRemove(image));
410
+ };
411
+ image.setAttribute('src', 'data:image/bmp;base64,' + base64);
412
+ });
413
+ }
414
+
415
+ return Promise.resolve(isIE11 ? getImgDataIE11() : getImgData()).then(function (imgData) {
416
+ if (!imgData || imgData.length < 10) {
417
+ return Promise.reject('Failed to get a snapshot');
418
+ }
419
+
420
+ return imgData;
421
+ });
422
+ };
423
+
424
+ /**
425
+ * Get statistics of the media subscription like bytes received.
426
+ *
427
+ * Used internally by TsMediaSubscriber.getStats
428
+ *
429
+ * @method
430
+ *
431
+ * @param { TsMediaSubscriber } subscriber
432
+ *
433
+ * @returns { Promise<ToBeDefined> } stats
434
+ *
435
+ * @see TsMediaSubscriber#getStats
436
+ */
437
+
438
+
439
+ TsWebRTCStream.prototype.getStats = function getStats(subscriber) {
440
+ var _this3 = this;
441
+
442
+ return new Promise(function (resolve) {
443
+ try {
444
+ var pc = _this3._getPeerConnection();
445
+
446
+ if (!pc) {
447
+ return resolve();
448
+ }
449
+
450
+ pc.getStats(_this3._mediaStream.getVideoTracks()[0]).then(function (reports) {
451
+ var matches = [];
452
+
453
+ reports.forEach(function (report) {
454
+ if (_.includes(['inbound-rtp', 'ssrc'], report.type)) {
455
+ matches.push(report);
456
+ }
457
+ });
458
+
459
+ return resolve({
460
+ video: _.head(matches)
461
+ });
462
+ });
463
+ } catch (e) {
464
+ // when this fails, it's most likely due ot no support for the getStats
465
+ // functionality, so we don't want to log this as a recurring error
466
+ resolve();
467
+ }
468
+ });
469
+ };
470
+ /* eslint-enable no-unused-vars */
471
+
472
+ TsWebRTCStream.prototype.getFullVideoStats = function getFullVideoStats() {
473
+ var _this4 = this;
474
+
475
+ return new Promise(function (resolve, reject) {
476
+ try {
477
+ var pc = _this4._getPeerConnection();
478
+
479
+ if (!pc) {
480
+ return reject('NO_PEER_CONNECTION');
481
+ }
482
+
483
+ return resolve(pc.getStats(_this4._mediaStream.getVideoTracks()[0]));
484
+ } catch (e) {
485
+ reject(e);
486
+ }
487
+ });
488
+ };
489
+
490
+ TsWebRTCStream.prototype.turnFlashlight = function turnFlashlight() {
491
+ var _this5 = this;
492
+
493
+ var isTorchOn = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
494
+
495
+ return new Promise(function (resolve, reject) {
496
+ if (_this5._torchDelayTimeOut) {
497
+ return reject('No tourch delay');
498
+ }
499
+
500
+ if (!navigator || !navigator.mediaDevices) {
501
+ return reject('No mediaDevices');
502
+ }
503
+
504
+ var track = _this5._mediaStream.getVideoTracks()[0];
505
+
506
+ if (!track) {
507
+ return reject('No track');
508
+ }
509
+ if (_this5._torchOffTimeOut) {
510
+ clearInterval(_this5._torchOffTimeOut);
511
+ _this5._torchOffTimeOut = null;
512
+ }
513
+
514
+ _this5._torchDelayTimeOut = setTimeout(function () {
515
+ var capatibilitiesAfter = {};
516
+
517
+ try {
518
+ capatibilitiesAfter = track.getCapabilities();
519
+ } catch (e) {
520
+ return reject('No capabilities');
521
+ }
522
+
523
+ if (!capatibilitiesAfter.torch) {
524
+ return reject('No tourch capabilities');
525
+ }
526
+
527
+ return track.applyConstraints({
528
+ advanced: [{
529
+ torch: isTorchOn
530
+ }]
531
+ }).then(function () {
532
+ _this5._torchDelayTimeOut = null;
533
+ resolve(true);
534
+ }).catch(function (e) {
535
+ _this5._torchDelayTimeOut = null;
536
+ console.error(e);
537
+ reject('Can\'t turn on the light, please try later!');
538
+ });
539
+ }, TORCH_DELAY);
540
+ });
541
+ };
542
+
543
+ TsWebRTCStream.prototype.setAutoLightOffTimer = function setAutoLightOffTimer(flashlightTimeout) {
544
+ var _this6 = this;
545
+
546
+ return new Promise(function (resolve) {
547
+ if (!flashlightTimeout) {
548
+ return reject('No timeout');
549
+ }
550
+
551
+ var track = _this6._mediaStream.getVideoTracks()[0];
552
+
553
+ if (!track) {
554
+ return reject('No track');
555
+ }
556
+
557
+ _this6._torchOffTimeOut = setTimeout(function () {
558
+
559
+ if (!track) {
560
+ return reject('No track');
561
+ }
562
+
563
+ track.applyConstraints({
564
+ advanced: [{
565
+ torch: false
566
+ }]
567
+ }).then(function () {
568
+ _this6._torchOffTimeOut = null;
569
+ resolve();
570
+ });
571
+ }, flashlightTimeout);
572
+ });
573
+ };
574
+
575
+ _createClass(TsWebRTCStream, [{
576
+ key: 'id',
577
+ get: function get() {
578
+ return this._mediaStream.id;
579
+ }
580
+
581
+ /**
582
+ * Unique name of the stream
583
+ *
584
+ * @member { String }
585
+ * @type { String }
586
+ */
587
+
588
+ }, {
589
+ key: 'name',
590
+ get: function get() {
591
+ return this._name;
592
+ }
593
+
594
+ /**
595
+ * Whether or not stream has enabled video tracks
596
+ *
597
+ * @member { Boolean }
598
+ * @type { Boolean }
599
+ */
600
+
601
+ }, {
602
+ key: 'hasVideo',
603
+ get: function get() {
604
+ var enabledVideoTracks = this._mediaStream.getVideoTracks().filter(function (track) {
605
+ return track.enabled;
606
+ });
607
+
608
+ return enabledVideoTracks.length > 0;
609
+ }
610
+
611
+ /**
612
+ * Whether or not stream has enabled audio tracks
613
+ *
614
+ * @member { Boolean }
615
+ * @type { Boolean }
616
+ */
617
+
618
+ }, {
619
+ key: 'hasAudio',
620
+ get: function get() {
621
+ var enabledAudioTracks = this._mediaStream.getAudioTracks().filter(function (track) {
622
+ return track.enabled;
623
+ });
624
+
625
+ return enabledAudioTracks.length > 0;
626
+ }
627
+ }]);
628
+
629
+ return TsWebRTCStream;
630
+ }(_stream.TsMediaStream);
631
+ //# sourceMappingURL=stream.js.map