HTML5 <audio> Safari直播与非</audio>

时间:2010-01-03 17:19:26

标签: audio safari html5 webkit

我正在尝试嵌入指向由PHP文件提供的MP3或OGG数据的HTML5音频元素。当我在Safari中查看页面时,会显示控件,但UI会显示“直播”。单击播放时,音频按预期启动。但是,一旦结束,我就无法通过点击播放再次开始播放。即使在audio元素上使用JS API并将currentTime设置为0也会因索引错误异常而失败。

我怀疑PHP脚本中的标题是问题,特别是缺少内容长度。但事实并非如此。响应头包括适当的Content-Length以指示音频具有有限的大小。此外,一切都在Firefox 3.5+中按预期工作。我可以多次点击音频元素上的播放来听到声音重播。

如果我从等式中删除PHP脚本并提供MP3文件的静态副本,那么在Safari中一切正常。

这是否意味着Safari使用查询参数处理音频src网址的方式与没有查询参数的网址不同?任何人都有运气好运吗?

我的简单示例页面是:

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <audio controls autobuffer>
      <source src="say.php?text=this%20is%20a%20test&format=.ogg" />
      <source src="say.php?text=this%20is%20a%20test&format=.mp3" />
    </audio>
  </body>
</html>

来自PHP脚本的HTTP标头:

HTTP/1.x 200 OK
Date: Sun, 03 Jan 2010 15:39:34 GMT
Server: Apache
X-Powered-By: PHP/5.2.10
Content-Length: 8993
Keep-Alive: timeout=2, max=98
Connection: Keep-Alive
Content-Type: audio/mpeg

来自直接文件访问的HTTP标头:

HTTP/1.x 200 OK
Date: Sun, 03 Jan 2010 20:06:59 GMT
Server: Apache
Last-Modified: Sun, 03 Jan 2010 03:20:02 GMT
Etag: "a404b-c3f-47c3a14937c80"
Accept-Ranges: bytes
Content-Length: 8993
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: audio/mpeg

我也尝试将Accept-Ranges标头硬编码到脚本中,但没有运气。

6 个答案:

答案 0 :(得分:34)

您是否可以在使用和不使用PHP脚本的情况下发布服务器发送的标头?我想知道PHP脚本是否正在发送与正常提供文件不同的Content-Type

type元素上指定source属性也是一个好主意,因此浏览器不必下载两个剪辑以确定它是否可以播放它们。

我无法重现你的问题。我尝试使用following test page在Safari 4.0.4和当前的WebKit中重新创建问题。我只是使用mod_rewrite基于参数而不是PHP来调度到不同的格式,但我不认为这应该有所作为,除非PHP正在修改文件。

<!DOCTYPE html>
<title>Auido test</title>
<audio controls autobuffer>
  <source src="gnossienne-no-1?foo=bar&format=.mp4">
  <source src="gnossienne-no-1?foo=bar&format=.ogg">
</audio>

你可以尝试我的例子,让我知道它是否适合你?

编辑啊。在更多地探讨它之后,似乎问题是由于奇怪的方式,Safari中的<audio>元素在尝试确定内容的大小时表现。

这是Safari在遇到指向直接从Apache提供的文件的<audio>元素时捕获数据包的摘录。正如您所看到的,它首先尝试获取媒体的前两个字节,可能是因为它可以返回Content-Length,可能还有其他标头。然后它试图获取整个事物。然后,莫名其妙地,它尝试再次获取前两个字节,但传递适当的缓存头以获得“304 Not Modified”响应。最后,仍然莫名其妙地,它再次获取文件的最后3440个字节。它在单独的TCP连接中完成所有这些操作,除了几次获取数据的开销之外,还会增加相当大的开销。

GET /stackoverflow/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1
Host: ephemera.continuation.org
Range: bytes=0-1
Connection: close
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Accept: */*
Accept-Encoding: identity
Cookie: [redacted]

HTTP/1.1 206 Partial Content
Date: Tue, 05 Jan 2010 02:12:48 GMT
Server: Apache
Last-Modified: Tue, 05 Jan 2010 02:02:08 GMT
ETag: "b2a80ad-11f6-47c6139aaa800"
Accept-Ranges: bytes
Content-Length: 2
Content-Range: bytes 0-1/4598
Connection: close
Content-Type: audio/mpeg

# 2 bytes of data

GET /stackoverflow/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1
Host: ephemera.continuation.org
Range: bytes=0-4597
Connection: close
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Accept: */*
Accept-Encoding: identity
Cookie: [redacted]

HTTP/1.1 206 Partial Content
Date: Tue, 05 Jan 2010 02:12:48 GMT
Server: Apache
Last-Modified: Tue, 05 Jan 2010 02:02:08 GMT
ETag: "b2a80ad-11f6-47c6139aaa800"
Accept-Ranges: bytes
Content-Length: 4598
Content-Range: bytes 0-4597/4598
Connection: close
Content-Type: audio/mpeg

# 4598 bytes of data

GET /stackoverflow/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1
Host: ephemera.continuation.org
Range: bytes=0-1
Connection: close
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Accept: */*
Accept-Encoding: identity
Cookie: [redacted]
If-None-Match: "b2a80ad-11f6-47c6139aaa800"
If-Modified-Since: Tue, 05 Jan 2010 02:02:08 GMT

HTTP/1.1 304 Not Modified
Date: Tue, 05 Jan 2010 02:12:49 GMT
Server: Apache
Connection: close
ETag: "b2a80ad-11f6-47c6139aaa800"

# no data

GET /stackoverflow/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1
Host: ephemera.continuation.org
Range: bytes=1158-4597
Connection: close
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Accept: */*
Accept-Encoding: identity
Cookie: [redacted]

HTTP/1.1 206 Partial Content
Date: Tue, 05 Jan 2010 02:12:49 GMT
Server: Apache
Last-Modified: Tue, 05 Jan 2010 02:02:08 GMT
ETag: "b2a80ad-11f6-47c6139aaa800"
Accept-Ranges: bytes
Content-Length: 3440
Content-Range: bytes 1158-4597/4598
Connection: close
Content-Type: audio/mpeg

# 3440 bytes of data

无论如何,关于如何处理PHP脚本的输出。在这里,Safari再次尝试下载前两个字节,但是您的脚本会忽略Range请求并返回整个事件。显然,WebKit不喜欢这样,所以它再次尝试,没有Range请求。同样,您的脚本会发送完整的内容。 Safari现在再次尝试,添加Icy-Metadata标头,表示它认为它正在下载流并希望发送流式元数据。它最终接受了它的输出,<audio>元素可以播放。

GET /say.php?text=this%20is%20a%20test&format=.mp3 HTTP/1.1
Host: tts.mindtrove.info
Range: bytes=0-1
Connection: close
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Accept: */*
Accept-Encoding: identity

HTTP/1.1 200 OK
Date: Tue, 05 Jan 2010 02:14:28 GMT
Server: Apache
X-Powered-By: PHP/5.2.10
Content-Length: 4598
Connection: close
Content-Type: audio/mpeg

# 4598 bytes of data

GET /say.php?text=this%20is%20a%20test&format=.mp3 HTTP/1.1
Host: tts.mindtrove.info
Connection: close
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Accept: */*

HTTP/1.1 200 OK
Date: Tue, 05 Jan 2010 02:14:28 GMT
Server: Apache
X-Powered-By: PHP/5.2.10
Content-Length: 4598
Connection: close
Content-Type: audio/mpeg

# 4598 bytes of data

GET /say.php?text=this%20is%20a%20test&format=.mp3 HTTP/1.1
Host: tts.mindtrove.info
Accept: */*
User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540
Icy-Metadata: 1
Connection: close

HTTP/1.1 200 OK
Date: Tue, 05 Jan 2010 02:14:28 GMT
Server: Apache
X-Powered-By: PHP/5.2.10
Content-Length: 4598
Connection: close
Content-Type: audio/mpeg

# 4598 bytes of data

总之,似乎Safari(或者更确切地说,Safari用于处理所有媒体和媒体下载的QuickTime)具有完全受到严重影响的下载媒体方法。您发送数据的方式可能是您没有响应Range请求的事实,这使得它认为您正在发送流媒体,导致它反复下载内容(尽管即使面对时也是如此)一个响应Range请求的服务器,它仍会执行多于它真正需要的请求。

我的建议是尝试适当回应Range请求;在提供媒体时,浏览器可能会使用它们来尝试最小化带宽,只需缓冲它们需要能够播放的内容(尽管具有autobuffer属性,表明您希望它们缓冲整件事,浏览器可能会忽略它)。我建议使用X-Sendfile让Apache处理文件,缓存和范围请求的服务,但是你似乎在Dreamhost上,没有安装mod_xsendfile,所以你要去必须推出自己的Range处理。

答案 1 :(得分:4)

为了记录,虽然Pochang和Chris都是正确的,你需要Content-Range标头来解决Safari中的这个问题,Chrome需要一个额外的标头,必须包含这个标头才能使currentTime正常工作:

header( 'Accept-Ranges: bytes');

请注意,您实际上不必正确响应请求的Range标头,您只需将其包含在响应中。

答案 2 :(得分:3)

老话题,但在2019年仍然有效。我们终于找到了解决方案...下面的PHP脚本示例将考虑Safari的“ Range”标头请求。

$bytes_total = strlen($data);
if (isset($_SERVER['HTTP_RANGE'])) {
    $byte_range = explode('-',trim(str_ireplace('bytes=','',$_SERVER['HTTP_RANGE'])));
    $byte_from = $byte_range[0];
    $byte_to = ($byte_range[1]+1);
    $data = substr($data,$byte_from,$byte_to);
    header('HTTP/1.1 206 Partial Content');
}
else {
    $byte_from = 0;
    $byte_to = $bytes_total;
}
$length = strlen($data); 
header('Content-type: '.$content_type);
header('Accept-Ranges: bytes');
header('Content-length: ' . $length);
header('Content-Range: bytes '.$byte_from.'-'.$byte_to.'/'.$bytes_total);
header('Content-Transfer-Encoding: binary');
print($data);

答案 3 :(得分:2)

我遇到了同样的问题。 关键点是 Content-Range 标头。 将它添加到mp3-output php后,一切正常。

答案 4 :(得分:2)

Pochang是正确的。在PHP响应中包含Content-Range标头将导致Safari正常运行。它还允许在Safari中搜索(media.currentTime = 0;)而不使用可怕的INDEX_SIZE_ERR,但不在Chrome中。

标题的PHP代码是:

$len = strlen( $data );
$shortlen = $len - 1;
header( 'Content-Range: bytes 0-'.$shortlen.'/'.$len);

答案 5 :(得分:0)

在2020年,这仍然是一个实际的问题。

仅添加Content-Range标头不起作用。

以下是我的实现(基于此处的一些答案)。

        $content_length = strlen($media_total);
        $total_bytes = $content_length;
        $content_length_1 = $content_length - 1;

        if (isset($_SERVER['HTTP_RANGE'])) {

            $byte_range = explode('-',trim(str_ireplace('bytes=','',$_SERVER['HTTP_RANGE'])));

            $byte_from = $byte_range[0];
            $byte_to = intval($byte_range[1]);
            $byte_to = $byte_to == 0 ? $content_length_1 : $byte_to;

            $media_total = substr($media_total,$byte_from,$byte_to);

            $content_length = strlen($media_total);

            header('HTTP/1.1 206 Partial Content');
        }
        else {
            $byte_from = 0;
            $byte_to = $content_length_1;
        }

        $content_range = 'bytes '.$byte_from.'-' . $byte_to . '/' . $total_bytes;

        header('Accept-Ranges: bytes');
        header("Content-Range: ".$content_range);
        header("Content-type: ".$type);
        header("Content-length: ".$content_length);
        header('Content-Transfer-Encoding: binary');

        echo $media_total;

        exit;

此处的原始问题:Timing problem for generated audio in some browsers