|
@@ -2,6 +2,9 @@
|
2
|
2
|
|
3
|
3
|
import React, { Component } from 'react';
|
4
|
4
|
|
|
5
|
+import { createAudioPlayErrorEvent, createAudioPlaySuccessEvent, sendAnalytics } from '../../../../analytics';
|
|
6
|
+import logger from '../../logger';
|
|
7
|
+
|
5
|
8
|
/**
|
6
|
9
|
* The type of the React {@code Component} props of {@link AudioTrack}.
|
7
|
10
|
*/
|
|
@@ -50,6 +53,11 @@ export default class AudioTrack extends Component<Props> {
|
50
|
53
|
*/
|
51
|
54
|
_ref: ?HTMLAudioElement;
|
52
|
55
|
|
|
56
|
+ /**
|
|
57
|
+ * The current timeout ID for play() retries.
|
|
58
|
+ */
|
|
59
|
+ _playTimeout: ?TimeoutID;
|
|
60
|
+
|
53
|
61
|
/**
|
54
|
62
|
* Default values for {@code AudioTrack} component's properties.
|
55
|
63
|
*
|
|
@@ -72,6 +80,7 @@ export default class AudioTrack extends Component<Props> {
|
72
|
80
|
|
73
|
81
|
// Bind event handlers so they are only bound once for every instance.
|
74
|
82
|
this._setRef = this._setRef.bind(this);
|
|
83
|
+ this._play = this._play.bind(this);
|
75
|
84
|
}
|
76
|
85
|
|
77
|
86
|
|
|
@@ -85,14 +94,7 @@ export default class AudioTrack extends Component<Props> {
|
85
|
94
|
this._attachTrack(this.props.audioTrack);
|
86
|
95
|
|
87
|
96
|
if (this._ref) {
|
88
|
|
- const { autoPlay, muted, volume } = this.props;
|
89
|
|
-
|
90
|
|
- if (autoPlay) {
|
91
|
|
- // Ensure the audio gets play() called on it. This may be necessary in the
|
92
|
|
- // case where the local video container was moved and re-attached, in which
|
93
|
|
- // case the audio may not autoplay.
|
94
|
|
- this._ref.play();
|
95
|
|
- }
|
|
97
|
+ const { muted, volume } = this.props;
|
96
|
98
|
|
97
|
99
|
if (typeof volume === 'number') {
|
98
|
100
|
this._ref.volume = volume;
|
|
@@ -181,6 +183,7 @@ export default class AudioTrack extends Component<Props> {
|
181
|
183
|
}
|
182
|
184
|
|
183
|
185
|
track.jitsiTrack.attach(this._ref);
|
|
186
|
+ this._play();
|
184
|
187
|
}
|
185
|
188
|
|
186
|
189
|
/**
|
|
@@ -193,10 +196,58 @@ export default class AudioTrack extends Component<Props> {
|
193
|
196
|
*/
|
194
|
197
|
_detachTrack(track) {
|
195
|
198
|
if (this._ref && track && track.jitsiTrack) {
|
|
199
|
+ clearTimeout(this._playTimeout);
|
|
200
|
+ this._playTimeout = undefined;
|
196
|
201
|
track.jitsiTrack.detach(this._ref);
|
197
|
202
|
}
|
198
|
203
|
}
|
199
|
204
|
|
|
205
|
+ _play: ?number => void;
|
|
206
|
+
|
|
207
|
+ /**
|
|
208
|
+ * Plays the uderlying HTMLAudioElement.
|
|
209
|
+ *
|
|
210
|
+ * @param {number} retries - The number of previously failed retries.
|
|
211
|
+ * @returns {void}
|
|
212
|
+ */
|
|
213
|
+ _play(retries = 0) {
|
|
214
|
+ if (!this._ref) {
|
|
215
|
+ // nothing to play.
|
|
216
|
+
|
|
217
|
+ return;
|
|
218
|
+ }
|
|
219
|
+ const { autoPlay, id } = this.props;
|
|
220
|
+
|
|
221
|
+ if (autoPlay) {
|
|
222
|
+ // Ensure the audio gets play() called on it. This may be necessary in the
|
|
223
|
+ // case where the local video container was moved and re-attached, in which
|
|
224
|
+ // case the audio may not autoplay.
|
|
225
|
+ this._ref.play()
|
|
226
|
+ .then(() => {
|
|
227
|
+ if (retries !== 0) {
|
|
228
|
+ // success after some failures
|
|
229
|
+ this._playTimeout = undefined;
|
|
230
|
+ sendAnalytics(createAudioPlaySuccessEvent(id));
|
|
231
|
+ logger.info(`Successfully played audio track! retries: ${retries}`);
|
|
232
|
+ }
|
|
233
|
+ })
|
|
234
|
+ .catch(e => {
|
|
235
|
+ logger.error(`Failed to play audio track! retry: ${retries} ; Error: ${e}`);
|
|
236
|
+
|
|
237
|
+ if (retries < 3) {
|
|
238
|
+ this._playTimeout = setTimeout(() => this._play(retries + 1), 1000);
|
|
239
|
+
|
|
240
|
+ if (retries === 0) {
|
|
241
|
+ // send only 1 error event.
|
|
242
|
+ sendAnalytics(createAudioPlayErrorEvent(id));
|
|
243
|
+ }
|
|
244
|
+ } else {
|
|
245
|
+ this._playTimeout = undefined;
|
|
246
|
+ }
|
|
247
|
+ });
|
|
248
|
+ }
|
|
249
|
+ }
|
|
250
|
+
|
200
|
251
|
_setRef: (?HTMLAudioElement) => void;
|
201
|
252
|
|
202
|
253
|
/**
|