feat(AudioTrack): retries for play()

This commit is contained in:
Hristo Terezov 2021-05-24 16:59:10 -05:00
parent 689bb3f226
commit 574994607c
2 changed files with 89 additions and 8 deletions

View File

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

View File

@ -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<Props> {
*/
_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<Props> {
// 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<Props> {
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<Props> {
}
track.jitsiTrack.attach(this._ref);
this._play();
}
/**
@ -193,10 +196,58 @@ export default class AudioTrack extends Component<Props> {
*/
_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;
/**