Cette histoire a commencé il y a environ 1,5 an. Il est associé à la lecture de musique dans divers navigateurs et plates-formes sur lesquels ils s'exécutent. Un chemin plein de
«douleur et de souffrance» de la prise de conscience qu'une tâche facile à première vue peut ne pas être aussi facile, et des détails «insignifiants» auxquels vous n'accordez pas d'importance au tout début, peuvent tout affecter.
Petits détails pour les plus curieux :)1. Téléchargement de données sur chaque piste suivante à partir du réseau.
2. Pour chaque élément audio: nouvel Audio () ou <audio> vous avez besoin de l'autorisation de l'utilisateur - une action de l'utilisateur sur la page.
Contexte
Tous ceux qui ont déjà écrit un lecteur audio pour les navigateurs au moins une fois dans leur vie ont probablement rencontré le problème de la compatibilité entre les navigateurs et des plates-formes.
Donc, tout en travaillant sur le nouveau MVP, je suis tombé sur diverses fonctionnalités concernant la lecture audio dans les navigateurs.
Et tout a commencé avec le fait qu'il était nécessaire de faire un fondu enchaîné en douceur de deux pistes pendant la lecture - c'est la première fonctionnalité. Notre équipe a voulu changer de piste comme à la radio. Et la deuxième fonctionnalité - chaque piste suivante est demandée au réseau.

Recherches
Ensuite, presque tous nos projets ont utilisé la bibliothèque Sound Manager 2.
Presque immédiatement, vous vous rendez compte que la lecture simultanée de deux fichiers audio sur des appareils mobiles ne fonctionne pas de la même manière partout!
Dans Chrome (version ~ 62) pour PC, les pistes ont été lues comme il se doit. Sur les appareils mobiles (également dans Chrome), la lecture des pistes a fonctionné, mais uniquement avec l'écran actif. Lorsque l'écran était verrouillé, la piste suivante après le lecteur actuel n'était pas lue. Comme pour iOS / macOS, la lecture a fonctionné de manière similaire. Plus d'informations peuvent être obtenues
ici - section "Single Audio Stream".
C'est ainsi que
j'ai commencé à
marcher sur trois mers à la recherche d'informations sur les fonctionnalités des navigateurs avec audio.
Ok, j'essaie une solution avec Web Audio sans utiliser de bibliothèques. Oui, cette technologie est destinée à d'autres fins: synthèse, traitement du son, jeux, etc., plutôt que de simplement jouer des morceaux. Mais pour le plaisir de l'expérience, il a fallu essayer, car il permet de composer des sons provenant de différentes sources en une seule
sortie audio - haut-parleurs / casque / haut-parleur de téléphone / etc. Il y a des gars qui
recherchent délibérément
les possibilités de jouer du son sur des appareils mobiles en utilisant l'API Web Audio.
Après la mise en œuvre, certaines nuances sont devenues claires.
Tout d'abord, vous devez attendre que la piste entière soit entièrement chargée. Avec une connexion Internet lente, les pauses seront perceptibles du fait que la deuxième piste peut ne pas avoir le temps de se charger avant la fin de la première piste. Les téléchargements complets peuvent être évités si vous utilisez un tas de balises audio HTML5 qui serviront de sources sonores pour Web Audio, mais dans ce cas, il devient à nouveau impossible de lire deux sons simultanément.
Deuxièmement, si vous téléchargez une piste sur le réseau en fragments et les décodez par programme, cela augmente la charge sur le processeur. C'était acceptable pour un PC, mais critique pour les appareils mobiles.
Troisièmement, il y a eu des problèmes de décodage. Si des fragments de fichiers mp3 / ogg / wav venaient au client, alors ces morceaux étaient
décodés et joués tranquillement. Mais si des morceaux du fichier mp4, qui servait de conteneur pour HE-AAC, venaient au navigateur, ils ne pourraient pas être décodés. Cela s'applique également dans une certaine mesure au navigateur Opera, dans lequel la lecture de fichiers MP3 est instable d'une version à l'autre - il se reproduit parfois, et il donne une erreur que ce format n'est pas pris en charge.
Quatrièmement, le nom de la piste n'a pas été affiché / n'a pas changé sur l'écran verrouillé sur une plaque avec un lecteur audio natif (sur iPad), incl. lors de la commutation entre les pistes. Peut-être dû au fait que les tests utilisaient l'iPad avec la version 9 d'iOS - il n'y en avait pas d'autre à l'époque.
En conséquence, à ce stade, Web Audio a dû être abandonné. Pourtant, le fondu enchaîné n'est pas pour les navigateurs, les compositions musicales standard de bonne qualité pèsent beaucoup.
Étant donné que nous refusons le fondu enchaîné, nous implémentons un fondu entrant et sortant simple, respectivement au début et à la fin de la piste musicale.
Le code de l'année précédente a été légèrement modifié et testé. À la suite des tests, diverses nuances sont apparues (présentées dans le tableau). Tout cela en utilisant la bibliothèque Sound Manager 2.

Nous ajoutons la journalisation de tous les événements pour déterminer le moment de transition entre les pistes et pour comprendre à quel moment ils arrêtent de jouer.

Activation de l'ongletDans Safari 9+, le son n'apparaît pas toujours lorsque l'onglet est activé.
De cela, on peut supposer que l'exécution de JS en arrière-plan est étranglée ou que le thread d'exécution s'arrête complètement (événements et
temporisateurs ). Cependant, il deviendra clair plus tard que c'était en partie une conclusion correcte. Ci-dessous, nous considérerons une autre nuance associée à la lecture des pistes et à la compréhension de la raison pour laquelle le son n'apparaît pas.
RemarquePour travailler avec progress (barre de progression), par exemple, pour le rendre pour une piste, il est bon d'utiliser requestAnimationFrame au lieu de setInterval / setTimeout. Vous pouvez éviter l'effet cumulatif lors de la désactivation (onglet d'arrière-plan), puis activer l'onglet et le geler temporairement, associé à tous les calculs et redessiner l'état d'avancement.
Au même moment, la question s'est posée: qu'en est-il de la lecture automatique des morceaux sur un PC et sur des appareils mobiles?
La lecture automatique fait référence au démarrage automatique de la lecture d'une piste sans aucune action de l'utilisateur lors du chargement d'une page.
Quant à Safari en ce qui concerne
la lecture automatique lors du chargement de la page, ce n'est pas possible, vous avez besoin
d'une interaction utilisateur avec la page, comme sur
les appareils mobiles . Cela s'applique à la fois au
contenu vidéo et au
contenu audio .
Et donc, à cette époque, il y avait ce qui suit:
- il est impossible (non souhaitable) de reproduire deux ou plusieurs sons simultanément;
- pour la pseudo «lecture automatique» de la piste, la permission de l'utilisateur est requise - la première interaction, plus tard, elle a été appelée «Vendre un doigt à l'appareil»;
- en arrière-plan (onglet fond / écran de verrouillage) JS (tout dépend du navigateur):
soit gèle complètement;
soit étranglement;
soit fonctionne de la même manière qu'avec l'onglet actif;
- Vous pouvez démarrer automatiquement la lecture sans son, mais il n'est pas clair pourquoi (pour le contenu audio)?
- quelque part loin, la pensée commence à se profiler, mais comment faire en sorte que JS continue à s'exécuter en arrière-plan?
Les autres bibliothèques qui implémentent les fonctions du lecteur partent du principe qu’il existe peut-être une solution à ce problème. Malgré le fait que de nombreux problèmes ont été observés sur GitHub avec une description des problèmes lors de la lecture de pistes dans divers navigateurs, il y avait encore de l'espoir que vous en arriviez au point: pourquoi cela ne fonctionne pas et comment le faire fonctionner. En fait, non ...
Quelques exemples de code avec démonstration vidéo du travail des bibliothèques:
- Sound Manager 2 - pages github, référentiel github , vidéo: macOS Safari 12 ; iOS Safari 10 avec écran déverrouillé
- Hurleur
Howler v2.0.9 - pages github, référentiel github , vidéo: macOS Safari 12 , iOS Safari 10
Howler v2.0.15 - pages github, référentiel github , vidéo: macOS Safari 12
Howler v2.1.1 - pages github, référentiel github , vidéo: macOS Safari 12 , iOS Safari 10
Pour macOS, l'enregistrement vidéo a été effectué sans son, vous devez donc regarder l'indicateur de volume - l'image du haut-parleur sur l'onglet.
D'autres exemples de vidéos sont disponibles dans le référentiel.
Dans l'exemple interactif de Howler v2.1.1 - parfois, vous pouvez entendre plusieurs sons en même temps, cela est dû à l'ajout d'un pool d'éléments audio déverrouillés par l'utilisateur (cela devrait être corrigé dans les futures versions de la bibliothèque).
Quelle est la raison de l'inopérabilité de ces bibliothèques?
J'ai écrit ci-dessus:
"En arrière-plan (onglet arrière-plan), JS se fige complètement ou subit une limitation .
" Voici donc un autre point qui apparaît: les bibliothèques du code utilisent la création de nouveaux objets audio via un nouvel Audio (). S'ils sont créés dynamiquement, c'est-à-dire si un objet audio existant n'est pas utilisé et que l'utilisateur n'interagit en aucune façon avec le site, l'onglet est inactif ou l'écran est verrouillé, certains navigateurs peuvent considérer que le son de cet élément audio ne doit pas être lu tant que l'onglet n'est pas réactivé ou que l'utilisateur ne le fait pas toute action.
Un exemple de test sur les
pages github et dans le
référentiel sur github en utilisant new Audio (). Vidéo:
macOS Safari 12 ;
iOS Safari 10 avec écran déverrouillé.
Il semble qu’aucune sorte d’outil universel n’existe et il est nécessaire de chercher une autre solution de compromis.Ensuite, nous nous asseyons avec les gars de l'équipe pour discuter, et qu'est-ce qui est vraiment important dans le travail du lecteur audio? Car il serait possible de poursuivre les expériences à l'infini, mais il faut avancer.
Premièrement, des points importants ont été identifiés qui ont empêché la réalisation du résultat souhaité:
- Safari sur macOS ne lit pas les pistes lorsque l'onglet est inactif;
- il n'y a aucune possibilité d'écouter de la musique en arrière-plan (lorsque l'écran est verrouillé) sur les smartphones exécutant iOS et Android, je voudrais éviter une redirection agressive des utilisateurs vers une application mobile (à l'avenir), car l'expérience précédente montre qu'une assez grande partie des utilisateurs ne veulent pas installer une application mobile ;
- le lecteur ne fonctionne pas correctement avec une liste de lecture dynamique, c'est-à-dire quand on ne sait pas à l'avance quelle sera la prochaine piste.
De plus, il a permis de formuler les objectifs nécessaires pour atteindre:
- fournir le lecteur en arrière-plan - dans divers navigateurs et sur diverses plates-formes;
- permettre à l'utilisateur de choisir ce qu'il veut utiliser: écouter de la musique sur le site ou dans l'application mobile;
- fournir la possibilité d'utiliser le joueur (ou l'approche) dans divers projets futurs.
Une nouvelle étape dans la recherche d'une solution au problème a commencé. À ce stade, diverses bibliothèques n'étaient plus utilisées; toutes les études ont été menées à l'aide de HTML5 Audio. Le résultat a été qu'une option a été trouvée en utilisant
des travailleurs dédiés . iOS n'a pas permis à cette décision de gagner à nouveau - la lecture en arrière-plan ne fonctionne pas, mais elle s'est avérée fonctionner sur Android (Chrome, Opera, Safari).
Exemple de test HTML5 Audio + Dedicated Workers
sur les pages github et dans
le référentiel github .
Lorsque le Worker est initialisé, des données sur la piste en cours sont demandées. Le travailleur envoie également un signal pour obtenir l'état d'avancement - la durée de lecture de la piste - à partir du flux principal et décide quand demander des données sur la piste suivante au réseau en fonction de ces données.

Toujours à cette époque, l'exemple suivant a été testé (
pages github, référentiel github ), lorsque la balise audio HTML5 est intégrée dans le DOM (vidéo:
macOS Safari 12 ,
iOS Safari 10 ) et il remplace simplement SRC lors du basculement entre les pistes. À ce jour, sur macOS dans 12 Safari, cet exemple fonctionne. Malheureusement, maintenant il n'y a aucun moyen de tester la fonctionnalité de cet exemple sur macOS dans Safari 10 et 11, mais à cette époque cet exemple ne fonctionnait pas pendant les tests (
politiques de lecture automatique, restrictions de lecture automatique ).
Pour résumer, pour iOS et macOS, le navigateur Safari ne considère pas qu'une nouvelle instance de l'élément audio soit activée par l'utilisateur si elle a été créée en arrière-plan dans un événement, par exemple, ajax, setTimeout, onended.
De plus, en ce qui concerne la lecture des pistes dans iOS Safari et iOS Chrome, il a été possible de lire des pistes en arrière-plan (lorsque l'écran est verrouillé) uniquement en utilisant
HLS . Pour les plates-formes iOS et macOS, ce format est standard et la diffusion est prise en charge par le système d'exploitation. Pour Android Chrome et Edge, une implémentation native est également disponible. Et pour les PC dans Chrome, vous pouvez utiliser des gestionnaires de logiciels, par exemple,
hls.js ,
Bitmovin Player , etc.
Un
lien vers le référentiel github fournit un exemple de code qui couvre le cas d'utilisation le plus simple - il suffit de lire le flux généré sur le serveur sans pouvoir revenir en arrière, passer à la piste suivante, etc. Des exemples sont présentés en utilisant: la balise audio, la balise vidéo, la bibliothèque hls.js et le lecteur de Bitmovin. Ce contenu nécessite Node.js.
Conclusions
Le premier point, malheureusement, en raison de la variété des navigateurs, il n'y a pas de solution universelle qui permettrait d'écouter de la musique dans les navigateurs aussi bien partout. Partout où il y a des limites et comme le montre la pratique, vous pouvez, tout à fait, vivre confortablement avec eux.
Deuxième point, il vaut parfois la peine de vérifier les cas limites le plus rapidement possible, par exemple, une implémentation native. Trouvez une sorte d'exigence minimale acceptable et vérifiez rapidement ses performances plutôt que de prendre n'importe quelle bibliothèque comme base. Cela permettra de mieux comprendre comment ces bibliothèques sont organisées en interne et pourquoi certaines fonctions fonctionnent ou ne fonctionnent pas. Sinon, vous pouvez exécuter assez loin dans le projet et après avoir réalisé que quelque chose ne va pas. Et il peut s'avérer que l'abandon de la bibliothèque coûtera assez cher. Une partie importante du code devra être réécrite.
Le troisième point, assurez-vous de prêter attention à l'audience de votre service - à partir de quels navigateurs et systèmes d'exploitation vos utilisateurs viennent. C'est assez facile à suivre en utilisant diverses métriques et systèmes de surveillance des erreurs. Une telle approche aidera à comprendre quelles plates-formes et navigateurs il est important de prendre en charge et lesquels peuvent être utilisés sans aucun effort.
Et enfin
J'annonce un petit concours lié à la lecture de musique sur iOS à l'aide de la technologie HLS.
La description peut être vue sur le
lien sur github .