From 574994607c1a2aed036c6a701e6c26a91285bd53 Mon Sep 17 00:00:00 2001 From: Hristo Terezov Date: Mon, 24 May 2021 16:59:10 -0500 Subject: [PATCH] feat(AudioTrack): retries for play() --- react/features/analytics/AnalyticsEvents.js | 30 +++++++++ .../base/media/components/web/AudioTrack.js | 67 ++++++++++++++++--- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/react/features/analytics/AnalyticsEvents.js b/react/features/analytics/AnalyticsEvents.js index 9616f966d..7a4b1f7be 100644 --- a/react/features/analytics/AnalyticsEvents.js +++ b/react/features/analytics/AnalyticsEvents.js @@ -696,6 +696,36 @@ export function createStartSilentEvent() { }; } +/** + * Creates an event which indicates that HTMLAudioElement.play has failed. + * + * @param {sting} elementID - The ID of the HTMLAudioElement. + * @returns {Object} The event in a format suitable for sending via sendAnalytics. + */ +export function createAudioPlayErrorEvent(elementID) { + return { + action: 'audio.play.error', + attributes: { + elementID + } + }; +} + +/** + * Creates an event which indicates that HTMLAudioElement.play has succeded after a prior failure. + * + * @param {sting} elementID - The ID of the HTMLAudioElement. + * @returns {Object} The event in a format suitable for sending via sendAnalytics. + */ +export function createAudioPlaySuccessEvent(elementID) { + return { + action: 'audio.play.success', + attributes: { + elementID + } + }; +} + /** * Creates an event which indicates the "start muted" configuration. * diff --git a/react/features/base/media/components/web/AudioTrack.js b/react/features/base/media/components/web/AudioTrack.js index 6583b4c7b..5189f31f9 100644 --- a/react/features/base/media/components/web/AudioTrack.js +++ b/react/features/base/media/components/web/AudioTrack.js @@ -2,6 +2,9 @@ import React, { Component } from 'react'; +import { createAudioPlayErrorEvent, createAudioPlaySuccessEvent, sendAnalytics } from '../../../../analytics'; +import logger from '../../logger'; + /** * The type of the React {@code Component} props of {@link AudioTrack}. */ @@ -50,6 +53,11 @@ export default class AudioTrack extends Component { */ _ref: ?HTMLAudioElement; + /** + * The current timeout ID for play() retries. + */ + _playTimeout: ?TimeoutID; + /** * Default values for {@code AudioTrack} component's properties. * @@ -72,6 +80,7 @@ export default class AudioTrack extends Component { // Bind event handlers so they are only bound once for every instance. this._setRef = this._setRef.bind(this); + this._play = this._play.bind(this); } @@ -85,14 +94,7 @@ export default class AudioTrack extends Component { this._attachTrack(this.props.audioTrack); if (this._ref) { - const { autoPlay, muted, volume } = this.props; - - if (autoPlay) { - // Ensure the audio gets play() called on it. This may be necessary in the - // case where the local video container was moved and re-attached, in which - // case the audio may not autoplay. - this._ref.play(); - } + const { muted, volume } = this.props; if (typeof volume === 'number') { this._ref.volume = volume; @@ -181,6 +183,7 @@ export default class AudioTrack extends Component { } track.jitsiTrack.attach(this._ref); + this._play(); } /** @@ -193,10 +196,58 @@ export default class AudioTrack extends Component { */ _detachTrack(track) { if (this._ref && track && track.jitsiTrack) { + clearTimeout(this._playTimeout); + this._playTimeout = undefined; track.jitsiTrack.detach(this._ref); } } + _play: ?number => void; + + /** + * Plays the uderlying HTMLAudioElement. + * + * @param {number} retries - The number of previously failed retries. + * @returns {void} + */ + _play(retries = 0) { + if (!this._ref) { + // nothing to play. + + return; + } + const { autoPlay, id } = this.props; + + if (autoPlay) { + // Ensure the audio gets play() called on it. This may be necessary in the + // case where the local video container was moved and re-attached, in which + // case the audio may not autoplay. + this._ref.play() + .then(() => { + if (retries !== 0) { + // success after some failures + this._playTimeout = undefined; + sendAnalytics(createAudioPlaySuccessEvent(id)); + logger.info(`Successfully played audio track! retries: ${retries}`); + } + }) + .catch(e => { + logger.error(`Failed to play audio track! retry: ${retries} ; Error: ${e}`); + + if (retries < 3) { + this._playTimeout = setTimeout(() => this._play(retries + 1), 1000); + + if (retries === 0) { + // send only 1 error event. + sendAnalytics(createAudioPlayErrorEvent(id)); + } + } else { + this._playTimeout = undefined; + } + }); + } + } + _setRef: (?HTMLAudioElement) => void; /**