Lo que puedes aprender al desarrollar un reproductor de audio para diferentes navegadores

Esta historia comenzó hace aproximadamente 1,5 años. Se asocia con la reproducción de música en varios navegadores y plataformas en las que se ejecutan. Un camino lleno de "dolor y sufrimiento" al darse cuenta de que una tarea fácil a primera vista puede no ser tan fácil, y detalles "insignificantes" a los que no le da importancia al principio, pueden afectar todo.

Detalles menores para los más curiosos :)
1. Descargando datos sobre cada próxima pista de la red.
2. Para cada elemento de audio: nuevo Audio () o <audio> necesita permiso del usuario, una acción del usuario en la página.

Antecedentes


Probablemente, todos los que alguna vez han escrito un reproductor de audio para navegadores al menos una vez en su vida han encontrado el problema de la compatibilidad entre navegadores y plataformas cruzadas.

Entonces, mientras trabajaba en el nuevo MVP, me encontré con varias características relacionadas con la reproducción de audio en los navegadores.

Y todo comenzó con el hecho de que era necesario hacer un fundido cruzado suave de dos pistas durante la reproducción: esta es la primera característica. Nuestro equipo quería hacer un cambio de pistas como en la radio. Y la segunda característica: cada pista posterior se solicita de la red.



Investigaciones


Luego, casi todos nuestros proyectos utilizaron la biblioteca Sound Manager 2.

¡Casi inmediatamente, te das cuenta de que reproducir dos archivos de audio simultáneamente en dispositivos móviles no funciona igual en todas partes!

En Chrome (~ 62 versión) para PC, las pistas se reproducían como deberían. En dispositivos móviles (también en Chrome), la reproducción de pistas funcionó, pero solo con la pantalla activa. Cuando la pantalla estaba bloqueada, no se reproducía la siguiente pista después del reproductor actual. En cuanto a iOS / macOS, la reproducción funcionó de manera similar. Se puede obtener más información aquí - sección "Flujo de audio único".

Entonces comenzó a caminar por tres mares buscando información poco a poco sobre las características de los navegadores con audio.

Ok, estoy probando una solución con Web Audio sin usar ninguna biblioteca. Sí, esta tecnología está destinada a otros fines: síntesis, procesamiento de sonido, para juegos, etc., en lugar de simplemente reproducir pistas. Pero por el bien del experimento, fue necesario intentarlo, ya que le permite componer sonidos de diferentes fuentes en una sola salida de audio : altavoces / auriculares / altavoz del teléfono / etc. Hay personas que están investigando a propósito las posibilidades de reproducir sonido en dispositivos móviles utilizando la API de audio web.

Después de la implementación, ciertos matices quedaron claros.

En primer lugar, debe esperar hasta que toda la pista esté completamente cargada. Con una conexión lenta a Internet, las pausas serán notables debido al hecho de que la segunda pista puede no tener tiempo para cargarse cuando finaliza la primera pista. Se pueden evitar las descargas completas si usa un montón de etiquetas de audio HTML5 que actuarán como fuentes de sonido para Web Audio, pero en este caso nuevamente es imposible reproducir dos sonidos simultáneamente.

En segundo lugar, si descarga una pista a través de la red en fragmentos y la decodifica mediante programación, esto aumenta la carga en la CPU. Era aceptable para una PC, pero crítico para dispositivos móviles.

En tercer lugar, hubo problemas con la decodificación. Si los fragmentos de archivos mp3 / ogg / wav llegaron al cliente, estas piezas se decodificaron y se reprodujeron en silencio. Pero si los fragmentos del archivo mp4, que actuaban como contenedor para HE-AAC, llegaban al navegador, entonces no podían decodificarse. Esto también se aplica en cierta medida al navegador Opera, en el que la reproducción de archivos MP3 es inestable de una versión a otra; a veces se reproduce y da un error de que este formato no es compatible.

En cuarto lugar, el nombre de la pista no se mostró / no cambió en la pantalla bloqueada en una placa con un reproductor de audio nativo (en iPad), incl. al cambiar de pista. Quizás debido al hecho de que las pruebas utilizaron el iPad con la versión 9 de iOS, no había otra en ese momento.

Como resultado, en esta etapa, Web Audio tuvo que ser abandonado. Aún así, el fundido cruzado no es para navegadores, las composiciones musicales estándar de buena calidad pesan bastante.

Como nos negamos al fundido cruzado, implementamos un fundido de entrada y salida simple al principio y al final de la pista de música, respectivamente.

El código del año anterior fue ligeramente modificado y probado. Como resultado de las pruebas, surgieron varios matices (que se muestran en la tabla). Todo esto utilizando la biblioteca Sound Manager 2.



Agregamos el registro de todos los eventos para determinar el momento de transición entre las pistas y para entender en qué punto dejan de reproducirse.



Activación de pestaña
En Safari 9+, el sonido no siempre aparece cuando se activa la pestaña.

A partir de esto, se puede suponer que la ejecución de JS en segundo plano está acelerada o que el hilo de ejecución se detiene por completo (eventos y temporizadores ). Sin embargo, quedará claro más tarde que esta fue en parte una conclusión correcta. A continuación, consideraremos otros 1 matices asociados con la reproducción de pistas y la comprensión de por qué no aparece el sonido.

Observación
Para trabajar con el progreso (barra de progreso), por ejemplo, renderizándolo para una pista, es bueno usar requestAnimationFrame en lugar de setInterval / setTimeout. Puede evitar el efecto acumulativo al desactivar (pestaña de fondo) y luego activar la pestaña y congelarla temporalmente, asociada con todos los cálculos y volver a dibujar el estado de progreso.

En el mismo momento, surgió la pregunta: ¿qué pasa con la reproducción automática de pistas en una PC y en dispositivos móviles?
La reproducción automática se refiere al inicio automático de la reproducción de una pista sin ninguna acción del usuario al cargar una página.
En cuanto a Safari con respecto a la reproducción automática cuando se carga la página, esto no es posible, necesita la interacción del usuario con la página, como en los dispositivos móviles . Esto se aplica tanto al contenido de video como al de audio .

Y así, en ese momento había lo siguiente:

  1. es imposible (no deseable) reproducir dos o más sonidos al mismo tiempo;
  2. para la pseudo "reproducción automática" de la pista, se requiere permiso del usuario: la primera interacción, luego se llamó "Vender dedo al dispositivo";
  3. en segundo plano (pestaña de fondo / pantalla de bloqueo) JS (todo depende del navegador):
    cualquiera se congela por completo;
    ya sea estrangulamiento;
    o funciona igual que con la pestaña activa;
  4. Puede iniciar automáticamente la reproducción sin sonido, pero no está claro por qué (para contenido de audio).
  5. En algún lugar lejano, el pensamiento comienza a surgir, pero ¿cómo hacer que JS continúe ejecutándose en segundo plano?

Otras bibliotecas que implementaron las funciones del reproductor asumieron que tal vez hay una solución para este problema. A pesar del hecho de que se vieron muchos problemas en GitHub con una descripción de los problemas al reproducir pistas en varios navegadores, todavía había esperanza de que estuvieras a punto de llegar al fondo: por qué no funciona y cómo hacerlo funcionar. Al final resultó que, no ...

Algunos ejemplos de código con video de demostración del trabajo de las bibliotecas:

  1. Sound Manager 2: páginas de github , repositorio de github , video: macOS Safari 12 ; iOS Safari 10 con pantalla desbloqueada
  2. Aullador
    Howler v2.0.9 - páginas de github , repositorio de github , video: macOS Safari 12 , iOS Safari 10
    Howler v2.0.15 - páginas de github , repositorio de github , video: macOS Safari 12
    Howler v2.1.1 - páginas de github , repositorio de github , video: macOS Safari 12 , iOS Safari 10

Para macOS, la grabación de video se realizó sin sonido, por lo que debe mirar el indicador de volumen: la imagen del altavoz en la pestaña.

Más videos de ejemplo están disponibles en el repositorio.

En el ejemplo interactivo para Howler v2.1.1: a veces puede escuchar varios sonidos al mismo tiempo, esto se debe a la adición de un conjunto de elementos de audio desbloqueados por el usuario (esto debería solucionarse en futuras versiones de la biblioteca).
¿Cuál es la razón de la inoperancia de estas bibliotecas?

Escribí arriba: "En el fondo (pestaña de fondo), JS se congela por completo o sufre una aceleración" . Entonces, aquí aparece otro punto: las bibliotecas en el código usan la creación de nuevos objetos de audio a través del nuevo Audio (). Si se crean dinámicamente, es decir Si no se usa un objeto de audio existente y el usuario no interactúa con el sitio de ninguna manera, la pestaña está inactiva o la pantalla está bloqueada, algunos navegadores pueden considerar que el sonido de este elemento de audio no debe reproducirse hasta que la pestaña esté activa nuevamente o el usuario no cualquier acción

Un ejemplo de prueba en páginas de github y en el repositorio en github usando el nuevo Audio (). Video: macOS Safari 12 ; iOS Safari 10 con pantalla desbloqueada.

Parece que no existe algún tipo de herramienta universal y es necesario buscar alguna otra solución de compromiso.

Luego nos sentamos con los chicos del equipo para discutir, y ¿qué es realmente importante en el trabajo del reproductor de audio? Porque sería posible continuar los experimentos hasta el infinito, pero necesitamos avanzar.

Primero, se identificaron puntos importantes que impidieron el logro del resultado deseado:

  1. Safari en macOS no reproduce pistas cuando la pestaña está inactiva;
  2. no hay posibilidad de escuchar música en segundo plano (cuando la pantalla está bloqueada) en teléfonos inteligentes con iOS y Android, me gustaría evitar la redirección agresiva de los usuarios a una aplicación móvil (en el futuro), ya que la experiencia previa muestra que una gran parte de los usuarios no quieren instalar una aplicación móvil ;
  3. el reproductor no funciona correctamente con una lista de reproducción dinámica, es decir cuando no se sabe de antemano cuál será la siguiente pista.

Además, permitió formular los objetivos necesarios para lograr:

  1. proporcionar el reproductor en segundo plano, en varios navegadores y en varias plataformas;
  2. permitir al usuario elegir qué usar: escuchar música en el sitio o en la aplicación móvil;
  3. proporcionar la capacidad de usar el jugador (o enfoque) en varios proyectos futuros.

Ha comenzado una nueva etapa en la búsqueda de una solución al problema. En esta etapa, ya no se usaban varias bibliotecas; todos los estudios se realizaron con HTML5 Audio. El resultado fue que se encontró una opción utilizando trabajadores dedicados . iOS no permitió que esta decisión volviera a ganar: la reproducción en segundo plano no funciona, pero resultó funcionar en Android (Chrome, Opera, Safari).

Ejemplo de prueba HTML5 Audio + Trabajadores dedicados en páginas de github y en el repositorio de github .

Cuando se inicializa el trabajador, se solicitan datos sobre la pista actual. El trabajador también envía una señal para obtener el estado de progreso (cuánto tiempo se reproduce la pista) desde la transmisión principal y decide cuándo solicitar datos sobre la próxima pista de la red en función de estos datos.



También en ese momento, se probó el siguiente ejemplo ( páginas github , repositorio github ), cuando la etiqueta de audio HTML5 está incrustada en el DOM (video: macOS Safari 12 , iOS Safari 10 ) y simplemente sustituye SRC cuando se cambia de pista. Hasta la fecha, en macOS en 12 Safari, este ejemplo funciona. Desafortunadamente, ahora no hay forma de probar la funcionalidad de este ejemplo en macOS en Safari 10 y 11, pero en ese momento este ejemplo no funcionó durante las pruebas ( políticas de reproducción automática , restricciones de reproducción automática ).

Para resumir, para iOS y macOS, el navegador Safari no considera que el usuario active una nueva instancia del elemento de audio si se creó en segundo plano dentro de un evento, por ejemplo, ajax, setTimeout, onended.

Además, con respecto a la reproducción de pistas en iOS Safari y iOS Chrome, se descubrió que era posible reproducir pistas en segundo plano (cuando la pantalla está bloqueada) solo usando HLS . Para las plataformas iOS y macOS, este formato es estándar y el sistema operativo admite la transmisión. Para Android Chrome y Edge, también está disponible una implementación nativa. Y para PC en Chrome, puede usar controladores de software, por ejemplo, hls.js , Bitmovin Player , etc.

Un enlace al repositorio de github proporciona un ejemplo de código que cubre el caso de uso más simple: simplemente reproducir la secuencia generada en el servidor sin la capacidad de rebobinar, cambiar a la siguiente pista, etc. Los ejemplos se presentan usando: la etiqueta de audio, la etiqueta de video, la biblioteca hls.js y el reproductor de Bitmovin. Este contenido requiere Node.js.

Conclusiones


El primer punto, desafortunadamente, debido a la variedad de navegadores, no existe una solución universal que permita escuchar música en navegadores igualmente bien en todas partes. En todas partes hay limitaciones y, como muestra la práctica, puedes vivir tranquilamente con ellas.

El segundo punto, a veces vale la pena verificar los casos límite lo más rápido posible, por ejemplo, una implementación nativa. Encuentre algún tipo de conjunto de requisitos mínimamente aceptable y compruebe rápidamente su rendimiento en lugar de tomar cualquier biblioteca como base. Esto le dará una mejor comprensión de cómo se organizan estas bibliotecas internamente y por qué ciertas funciones funcionan o no. De lo contrario, puede correr bastante lejos en el proyecto y después de darse cuenta de que algo va mal. Y puede resultar que abandonar la biblioteca sea bastante costoso. Será necesario reescribir una parte importante del código.

El tercer punto, asegúrese de prestar atención a la audiencia de su servicio: de qué navegadores y sistemas operativos provienen sus usuarios. Esto es bastante fácil de rastrear usando varias métricas y sistemas de monitoreo de errores. Tal enfoque ayudará a comprender qué plataformas y navegadores es importante admitir, y cuáles se pueden usar sin ningún esfuerzo.

Y finalmente


Estoy anunciando un pequeño concurso relacionado con la reproducción de música en iOS usando la tecnología HLS.

La descripción se puede ver en el enlace en github .

Source: https://habr.com/ru/post/438952/


All Articles