HTML 5 / caching de audio QuickTime en Safari en iOS

Estoy desesperadamente tratando de encontrar una solución para una aplicación web que tiene que ejecutarse en un iOS-Safari (por ejemplo, en iPad, iPad2 y iPhone 4):

Es una aplicación web que escribí hace un time que permite al usuario search y escuchar muestras de música cortas (MP3s, todos de ~ 100 kB a ~ 1.5 MB). El reproductor de audio está basado en Flash, por lo que no funciona en dispositivos iOS en este momento y tendré que implementar una alternativa en HTML 5 o con un object QuickTime "directo".

Mis alternativas HTML 5 y QuickTime para dispositivos iOS funcionan bien hasta ahora, pero hay un problema importante al que no puedo encontrar una solución:

A diferencia de Flash, la mayoría de los browseres compatibles con HTML 5 en Windows Safari en mi iPad 2 no almacenarán los audiofiles en el browser después de cargarlos y reproducirlos, ni con tags de audio HTML 5 ni con QuickTime-Object. Cada vez que cargo un file de audio para reproducirlo desde el server (con JavaScript-Commands, así que sin cambiar o volver a cargar la página completa) se vuelve a download completamente.

Si un usuario escucha la muestra A y luego la muestra B, Safari olvidó haber reproducido la muestra A y descarga todo el MP3 nuevamente si me gusta volver a escucharla. En un dispositivo mobile con una banda potencialmente estrecha con este comportamiento está fuera de cuestión.

¿Hay una manera de almacenar audiofiles descargados abiertos por HTML 5 o QuickTime en el caching de Safari para que recuerde que ya los ha descargado, como si almacena en caching los "files web" habituales como HTML, CSS o imágenes JPEG o si Flash almacena dichos objects en su caching local?

Mi primer bash fue intentar usar el caching de la aplicación con un file de manifiesto, aunque este no es realmente el propósito para mi aplicación … No tengo un set estático de files que quiero tener almacenados en caching o "disponibles fuera de línea" – Solo quiero almacenar en caching MP3 que el usuario ha jugado todavía.

Debería ser posible usar un "manifiesto dynamic": uno que sea analizado por el module Apache PHP y enumere los files jugados hasta ahora de una session PHP, algo como esto:

session_start(); header("Content-Type: text/cache-manifest, charset=UTF-8"); echo "CACHE MANIFEST\n"; foreach($_SESSION['playedSongs'] as $song) { echo $song."\n"; } 

Entonces, cada vez que se carga / reproduce una canción, puedo acceder a la session PHP con AJAX, insert el nombre del file reproducido y actualizar manualmente el manifiesto llamando a window.applicationCache.update () o .swapCache ().

Hay dos problemas con esto:

Primero que todo: no funciona. Y ni siquiera llegué al punto de intentar usar un manifiesto dynamic:

 <!DOCTYPE html> <html manifest="cache.manifest"> <head> <title>Test</title> <script type="text/javascript"> function playStuff(id) { if(id == 1) { window.document.getElementById("audio").innerHTML = '<audio controls preload="automatic" autobuffer><source src="song01.mp3" type="audio/mp3" /></audio>'; } else if(id == 2) { window.document.getElementById("audio").innerHTML = '<audio controls preload="automatic" autobuffer><source src="song02.mp3" type="audio/mp3" /></audio>'; } } </script> </head> <body> <div id="audio"></div><br /> <br /> <input type="button" value="playStuff(1)" onclick="playStuff(1)" /> <input type="button" value="playStuff(2)" onclick="playStuff(2)" /> </body> </html> 

El caching.manifest se ve así:

 CACHE MANIFEST song01.mp3 song02.mp3 

y se devuelve correctamente de Apache como "text / cache-manifest" agregando

 AddType text/cache-manifest manifest 

al .htaccess de este directory.

Los loggings de Apache muestran claramente que Safari (respectivamente "AppleCoreMedia") no se preocupa por el caching de la aplicación cuando se trata de files de audio:

Safari parece reconocer el manifiesto y, de hecho, precargar los files:

 192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/index2.html HTTP/1.1" 200 2619 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" 192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/cache.manifest HTTP/1.1" 200 79 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" 192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/cache.manifest?%3E HTTP/1.1" 200 79 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" 192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/song02.mp3 HTTP/1.1" 200 120525 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" 192.168.0.40 - - [23/Jul/2011:12:45:46 +0200] "GET /websql/song01.mp3 HTTP/1.1" 200 120525 "-" "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" 

Hasta este punto no hice más que abrir mi aplicación de testing en Safari.

Playing song01.mp3:

 192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 2 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:47:25 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:47:29 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:47:29 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 

Playing song2.mp3:

 192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 2 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:04 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:05 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:05 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 

Reproducción de song1.mp3 nuevamente:

 192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:38 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:40 +0200] "GET /websql/song01.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:48:40 +0200] "GET /websql/song01.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 

Reproducción de canción2.mp3 nuevamente:

 192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:49:12 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:49:13 +0200] "GET /websql/song02.mp3 HTTP/1.1" 304 - "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 192.168.0.40 - - [23/Jul/2011:12:49:13 +0200] "GET /websql/song02.mp3 HTTP/1.1" 206 120525 "-" "AppleCoreMedia/1.0.0.8J2 (iPad; U; CPU OS 4_3_3 like Mac OS X; de_de)" 

Cada file se descarga de nuevo completamente al reproducirlo. Entonces, "AppleCoreMedia" (sea lo que sea exactamente, el complemento QuickTime que se activa con el elemento de audio HTML 5, supongo) no tiene acceso a la memory caching de la aplicación o simplemente no se da count de los files en ella . Entonces, si cambio mi iPad a "Modo Avión" ahora, Safari no puede acceder / cargar / reproducir los files.

También traté de usar un object QuickTime en lugar de una label de audio HTML 5 (por lo que sé que el audio y video HTML 5 en Safari siempre usa QuickTime) y controlarlo con algo como:

 document.movie1.SetURL('song02.mp3'); 

Nada cambia, es como usar audio HTML 5 y todo se vuelve a download al cargarlo / reproducirlo.

E incluso si esto funcionara, todavía habría un problema:

Para implementar correctamente, tendría que cargar el file MP3 en el caching de la aplicación antes de reproducirlo. Al hacer esto parece imposible mostrar un progreso "real": el ProgressEvent que se dispara desde el caching de la aplicación después de actualizarlo no parece proporcionar ninguna información sobre los datos cargados hasta ahora y el tamaño completo de un file. Es solo "Archivo 1 a partir de 2" y así sucesivamente, y no un progreso "real" donde pude determinar algo como: "100 kB de 1.2 MB cargados", como puedo hacer con el elemento de audio.

Todos los demás enfoques de almacenamiento como Web SQL / Web Database o Local Storage tampoco son de ayuda:

No veo ninguna forma de get los datos de MP3 en el almacenamiento local o la database web y / o sacarlos de nuevo para reproducirlos. El elemento HTML 5 Canvas tiene una function toDataURL () para producir una representación codificada en Base64 y usarla para el almacenamiento: el elemento Audio no parece tener nada parecido a esto.

Mi último enfoque realmente "sucio" estaba tratando de cargar MP3s codificados con Base64 "manualmente" con una combinación de AJAX y PHP: un script PHP emite una representación de Base64 de un file MP3 y es cargado por AJAX, por lo que podría almacene la representación Base64, por ejemplo, como almacenamiento local o en la database web:

 $infile = 'song01.mp3'; $contents = file_get_contents($infile); $base64 = base64_encode($contents); $audio = 'data:audio/mp3;base64,'.$base64; echo $audio; 

Intenté usar el AJAX responseText resultante como argumento de origen en una label de fuente de audio. Suprise: No funciona en Safari en mi iPad 2, el reproductor simplemente no puede cargar el "file", aunque esto funciona bien en Chrome en Windows. Posiblemente una limitación de tamaño para Base64-URI en Safari / iOS?

Y otra vez: incluso si esto funcionaba en iOS / Safari, no sé de una manera de determinar un progreso real de una consulta AJAX …

Lo último en lo que estaba pensando no era replace las tags de audio o fuente al cargar una canción, sino dejarlas en la estructura DOM, verificar si ya está allí cuando se carga una canción y simplemente agregar una nueva label de audio si una la canción no se ha cargado todavía No funciona … Si agrega varias instancias de jugador dinámicamente (de nuevo, no importa si hay tags HTML5 o objects QuickTime) en lugar de "sobrescribirlos", Safari olvida haber cargado nunca el primer MP3 tan pronto como lo hizo inserte un nuevo audio o QuickTime-Element en el tree DOM; ¡ni siquiera tiene que cargar / reproducir algo en la nueva instancia! La reproducción repetida sin una recarga completa del file funciona siempre y cuando no reproduzca ni inserte nada relacionado con audio / medios. BTW: solo el uso de objects de audio en JavaScript y "savelos" en una matriz no funciona o no hace que Safari guarde nada.

¡Esto produce un montón de tráfico innecesario y toma mucho time innecesario si estás en una networking celular con un ancho de banda bajo!

Estoy trabajando en este problema durante tres días sin siquiera acercarme a una solución …

¿Algunas ideas?

Estoy casi seguro de que esto es por layout y no puede ser anulado; las cosas de CoreMedia intencionalmente transmiten el file según sea necesario y lo arrojan sin cachearlo cuando se descarta el jugador. Esto se debe al almacenamiento limitado, la duración de la batería, etc. ya que la gran mayoría del time, carga un file multimedia, lo reproduce sin embargo muchas veces, luego se deshace de él. Siempre que el tipo de contenido sea un tipo de medio, esto sucederá.

Otra publicación hace reference a la idea de codificar los datos en un PNG para que el browser lo almacene en caching, pero no he intentado eso.

Puede intentar fusionar varias muestras de audio en un file y luego transmitir los times de inicio / finalización para cada muestra (un índice básicamente); entonces puede cargar el file en un solo reproductor de audio y saltar a la location necesaria y jugar solo durante el time especificado. Si esa location aún no se ha descargado, creo que Safari usará los encabezados de range para avanzar en el file (pero eso puede depender exactamente de qué tipo de contenedor y si el contenedor tiene un índice).

Otra alternativa sería utilizar un server de medios de transmisión que puede reproducir audio de manera dinámica. Simplemente haga que la transmisión se active cuando el reproductor esté activado pero transmita silencio (el protocolo correcto debería usar un ancho de banda mínimo para este caso), luego las requestes de una muestra activan el server de transmisión para reproducir esa muestra. No es ideal o muy eficiente por desgracia.

En caso de que alguien todavía se esté tirando de esto (recientemente perdí un fin de semana), Safari en iOS 6 tiene una Web Audio API.

https://developer.apple.com/technologies/ios6/

Prueba este truco que he visto en mi twitter hace algunas semanas, pero nunca tuve time de intentarlo: agrega un iframe y establece la fuente en la URL del file multimedia. Suena raro, pero fue de un tweet muy popular de JS …

Ah, y lo veo aquí: ¿Cómo puedo reproducir files multimedia en iOS> = 4.2.1 Mobile Safari?

Vale la pena intentarlo