Browse Source

feat(AudioTrack): retries for play()

master
Hristo Terezov 3 years ago
parent
commit
574994607c

+ 30
- 0
react/features/analytics/AnalyticsEvents.js View File

@@ -696,6 +696,36 @@ export function createStartSilentEvent() {
696 696
     };
697 697
 }
698 698
 
699
+/**
700
+ * Creates an event which indicates that HTMLAudioElement.play has failed.
701
+ *
702
+ * @param {sting} elementID - The ID of the HTMLAudioElement.
703
+ * @returns {Object} The event in a format suitable for sending via sendAnalytics.
704
+ */
705
+export function createAudioPlayErrorEvent(elementID) {
706
+    return {
707
+        action: 'audio.play.error',
708
+        attributes: {
709
+            elementID
710
+        }
711
+    };
712
+}
713
+
714
+/**
715
+ * Creates an event which indicates that HTMLAudioElement.play has succeded after a prior failure.
716
+ *
717
+ * @param {sting} elementID - The ID of the HTMLAudioElement.
718
+ * @returns {Object} The event in a format suitable for sending via sendAnalytics.
719
+ */
720
+export function createAudioPlaySuccessEvent(elementID) {
721
+    return {
722
+        action: 'audio.play.success',
723
+        attributes: {
724
+            elementID
725
+        }
726
+    };
727
+}
728
+
699 729
 /**
700 730
  * Creates an event which indicates the "start muted" configuration.
701 731
  *

+ 59
- 8
react/features/base/media/components/web/AudioTrack.js View File

@@ -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
     /**

Loading…
Cancel
Save