iOS中Safari浏览器的HTML 5 / QuickTime音频缓存

15

我正在努力寻找一个解决方案,使一个网络应用程序能够在iOS Safari上运行(例如在iPad、iPad2和iPhone4上):

这是我前段时间写的一个网络应用程序,允许用户搜索和收听短音乐样本(MP3格式,大小约为100KB至1.5MB)。音频播放器是基于Flash的,因此目前无法在iOS设备上使用,我必须使用HTML 5或者“直接”QuickTime对象来实现替代方案。

到目前为止,我的HTML 5和QuickTime在iOS设备上的替代方案都很好用,但我遇到了一个主要问题:与Flash和大多数支持HTML 5的Windows浏览器不同,在我的iPad 2上Safari无法在加载和播放后将音频文件存储在浏览器缓存中-无论是使用HTML5音频标记还是QuickTime对象。每次我从服务器加载音频文件进行播放(通过JavaScript命令,因此不需要更改或重新加载整个页面)时,它都会被完全下载。

如果用户先听取样品A,然后再听取样品B,Safari会忘记已经播放过样品A,并且如果我想再次收听它,则会再次下载整个MP3文件。在具有潜在窄带宽的移动设备上,这种行为是不能接受的。

是否有一种方法可以将HTML 5或QuickTime打开的已下载音频文件存储在Safari的缓存中,使其记住已经下载过它们-就像缓存HTML、CSS或JPEG图像等常规“web文件”一样,或者像Flash将这些对象存储在其本地缓存中?

我的第一次尝试是尝试使用带有清单文件的应用程序缓存-虽然这不是我的应用程序的真正目的...我没有一个静态的文件集,我想要缓存或“离线可用”-我只想缓存用户已经播放过的MP3文件。

应该可以使用“动态清单”:通过Apache PHP模块解析并从PHP会话中列出到目前为止已播放的文件-类似于下面的方式:

session_start();

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

所以每当一首歌曲被加载/播放时,我可以通过AJAX访问PHP会话,插入已播放文件的文件名并通过调用window.applicationCache.update()或.swapCache()手动更新清单。

这样做存在两个问题:

首先:它不起作用。而且我甚至没有尝试使用动态清单:

<!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>

缓存清单(cache.manifest)看起来像这样:

CACHE MANIFEST

song01.mp3
song02.mp3

通过添加,Apache正确返回为"text/cache-manifest"

AddType text/cache-manifest manifest

将此目录的 .htaccess 添加以下内容。

Apache 日志清楚地显示 Safari(或"AppleCoreMedia")在处理音频文件时并不关心应用程序缓存:

Safari 本身似乎确认了清单,确实预加载了这些文件:

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"

到目前为止,我只是在Safari中打开了我的测试应用程序。

播放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)"

播放歌曲 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)"

再次播放 song1.mp3:

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)"

再次播放song2.mp3:

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)"

播放文件时每次都要完整地重新下载。因此,“AppleCoreMedia”(无论这是什么,我想它可能是由HTML 5音频元素触发的QuickTime插件?)要么无法访问应用程序缓存,要么只是没有意识到其中的文件。因此,如果我现在将我的iPad切换到“飞行模式”,Safari将无法访问/加载/播放文件。

我还尝试使用QuickTime对象而不是HTML 5音频标记(据我所知,在Safari中,HTML 5音频和视频始终使用QuickTime?)并使用以下内容控制它:

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

没有什么变化,就像使用HTML 5音频一样,加载/播放时会重新下载所有内容。

即使这样可以工作,仍然存在一个问题:

要正确实现这一点,我需要在播放之前在应用程序缓存中加载MP3文件。但是,在这样做时似乎无法显示“真正”的进度:从更新应用程序缓存后触发的ProgressEvent似乎不提供有关已加载数据和文件完整大小的任何信息。只是“文件1/2”等,而不是像音频元素一样的“真正”进度,我可以确定类似于:“已加载1.2 MB中的100 KB”。

所有其他的存储方法,如Web SQL/Web数据库或本地存储也都没有帮助:

我看不到将MP3数据放入本地存储或Web数据库中并再次取出以进行播放的任何方法。 HTML 5画布元素具有toDataURL()函数以生成Base64编码表示形式并用于存储-音频元素似乎没有这样的功能。

最后一种“有些糟糕”的方法是尝试手动加载通过AJAX和PHP组合的Base64编码-MP3:PHP脚本输出MP3文件的Base64表示形式,并由AJAX加载,因此我可以将Base64表示形式存储为本地存储或Web数据库:

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

我尝试使用返回的 AJAX responseText 作为 audio source 标签的 source 参数。令人惊讶的是,在我的 iPad 2 上,Safari 无法加载这个“文件”,尽管在 Windows 上的 Chrome 中可以正常工作。可能是 Safari / iOS 上 Base64 URI 的大小限制导致的?

而且,即使在 iOS / Safari 中有效,我也不知道如何从 AJAX 查询中确定真实的进度...

我最后想到的事情是,在加载歌曲时不替换音频或 source 标签,而是将它们留在 DOM 结构中,检查是否已经存在当加载一首歌曲时,并且如果还没有加载过歌曲就添加一个新的音频标签。但是这并不起作用......

如果动态添加多个播放器实例(无论是 HTML5 标记还是 QuickTime 对象),而不是“覆盖”它们,Safari 就会忘记之前加载的第一个 MP3 文件,只要您在 DOM 树中插入新的音频或 QuickTime 元素 — 即使您甚至不必加载 / 播放新实例中的任何内容!只要不播放或插入其他音频 / 媒体相关元素,重复播放而不完全重新加载文件就可以正常工作。顺便说一下:仅使用 JavaScript 中的 Audio 对象并将其“保存”到数组中也不起作用 / 不会使 Safari 缓存任何内容。

如果您处于低带宽的蜂窝网络中,这会产生大量不必要的流量并且需要很长时间!

我已经花了三天时间解决这个问题,但仍然没有找到解决方案...

有任何想法吗?


2
+1 为了表现力 :) 我重新标记了 iPhone,因为它不是 iPad 特定的,而 iPhone 标记被更多人订阅。 - Kay
也许这里包含了一些有趣的链接和信息 https://dev59.com/13I-5IYBdhLWcg3w6tFR - Kay
肯定有一些有趣的细节,Kay,但没有关于IOS问题的答案。 - Peter J
2
我认为你的方法不太对。本地存储和应用程序缓存并不适合大型音频文件。我想我们仍然可以通过普通缓存找到解决方案。服务器在提供音频文件时发送了哪些头部信息?你能否运行 curl -v URL 并在此处发布头部信息? - Matej Balantič
1
同意@Matej的观点 - 你确定你的服务器发送了正确的HTTP头来告诉Safari缓存数据吗?如果没有,这种重复下载正是我所期望看到的行为。 - Malcolm Box
3个回答

2
我几乎可以确定这是设计上的问题,无法被覆盖;CoreMedia有意在播放器关闭时按需流式传输文件并将其丢弃而不进行缓存。由于存储空间、电池寿命等方面的限制,大多数情况下,您只需加载一个媒体文件,播放多少次,然后将其删除。每当内容类型为媒体类型时,都会发生这种情况。
另一篇文章提到了在PNG中编码数据以使浏览器缓存它的想法,但我还没有尝试过。
您可以尝试将各种音频样本合并成一个文件,然后传输每个样本的开始/停止时间(基本上是索引);然后您可以在单个音频播放器中加载文件,并跳转到必要的位置并仅播放指定时间。如果该位置尚未下载,我相信Safari将使用范围标头向前跳转文件(但这取决于容器的类型以及容器是否具有索引)。
另一种选择是使用动态播放音频的流媒体服务器。当播放器启用时,只需让流保持静默状态(正确的协议应该对此情况使用最小带宽),然后对样本的请求触发流媒体服务器播放该样本。不幸的是,这并不理想,也不是非常高效。

1

你不应该在公共场合讨论iOS的beta版本功能。 - Till
为什么不呢?这是在Apple.com上的公共信息:“iOS 6上Safari的新功能允许您使用Web Audio API为交互式Web应用程序创建音频”。这是个好消息,我正在为iPad构建基于HTML 5的儿童游戏,但声音卡顿问题一直困扰着我。 - SondreB

0

尝试一下我几周前在 Twitter 上看到的技巧,但实际上从未有时间尝试:添加一个 iframe,并将源设置为媒体文件的 URL。听起来很奇怪,但这是来自一个非常受欢迎的 JS 专家的推文...

哦,我在这里看到了:如何在 iOS >= 4.2.1 移动 Safari 中自动播放媒体?

绝对值得一试


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接