HTML5 <audio> Сафари в прямом эфире против нет

Я пытаюсь встроить аудио-элемент HTML5, указывающий на данные MP3 или OGG, обслуживаемые файлом PHP. Когда я просматриваю страницу в Safari, элементы управления отображаются, но пользовательский интерфейс говорит "Live Broadcast". Когда я нажимаю кнопку воспроизведения, звук начинается, как ожидалось. Однако, как только это закончится, я не могу запустить его снова, нажав кнопку воспроизведения. Даже использование JS API на аудиоэлементе и установка currentTime на 0 с ошибкой исключения индекса.

Я подозревал, что заголовки из PHP script были проблемой, особенно отсутствием длины содержимого. Но это не так. Заголовки ответов содержат правильный Content-Length, чтобы указать, что звук имеет конечный размер. Кроме того, все работает как ожидается в Firefox 3.5+. Я могу несколько раз щелкнуть по звуковому элементу, чтобы услышать звуковой повтор.

Если я удалю PHP script из уравнения и отправлю статическую копию файла MP3, все будет хорошо работать в Safari.

Означает ли это, что Safari обрабатывает URL-адреса аудио src с параметрами запроса иначе, чем URL-адреса, которые их не имеют? Кому-нибудь удастся заставить это работать?

Моя простая страница с примерами:

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

Заголовки HTTP из PHP script:

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 в script, но не повезло.

Ответ 1

Можете ли вы разместить заголовки, отправленные сервером как с PHP, так и без него? script? Мне интересно, если PHP скрипт отправляет другой Content-Type, чем обычно обслуживает файлы.

Также было бы полезно указать атрибут type для элементов source, поэтому браузеру не нужно загружать оба клипа, чтобы определить, может ли он воспроизводиться.

Я не могу воспроизвести вашу проблему. Я попытался воссоздать проблему в 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>

Можете ли вы попробовать мой пример и сообщить мне, если он работает для вас?

изменить. Похоже, что это немного связано с тем, что проблема связана с тем, что элемент <audio> в Safari ведет себя в попытке определить размер содержимого.

Здесь выдержка из пакета захвата Safari при столкновении с элементом <audio>, указывающим на файл, который подается непосредственно из Apache. Как вы можете видеть, сначала он пытается извлечь первые два байта носителя, предположительно, чтобы получить длину 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 script. Здесь Safari снова пытается загрузить первые два байта, но ваш script игнорирует запрос Range и возвращает все это. По-видимому, WebKit этого не нравится, и поэтому он пытается снова, без запроса Range. Опять же, ваш script отправляет полное содержимое. 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 (или, точнее, QuickTime, которое Safari использует для обработки всех носителей и загрузки мультимедиа), имеет абсолютно бесстрашный подход к загрузке медиа. Что-то в том, как вы отправляете свои данные обратно, возможно, тот факт, что вы не отвечаете на запросы Range, заставляет думать, что вы отправляете потоковые медиа, заставляя его загружать контент повторно (хотя даже при столкновении с сервером который отвечает на запрос Range, он по-прежнему выполняет еще несколько запросов, чем это действительно необходимо).

Моим советом было бы попытаться ответить соответствующим образом на Range запросы; при обслуживании носителей, браузеры, скорее всего, будут использовать их, чтобы попытаться минимизировать пропускную способность, за счет буферизации столько, сколько им нужно, чтобы иметь возможность играть (хотя есть атрибут autobuffer, который указывает, что вы хотели бы, чтобы они забуферили все это, браузеры могут игнорировать это). Я бы рекомендовал использовать X-Sendfile, чтобы Apache имел дело с обслуживанием файлов, кешированием и запросами диапазона, но вы, кажется, находились на Dreamhost, у которого нет mod_xsendfile, поэтому вам придется сворачивать ваши собственная обработка Range.

Ответ 2

Для записи, в то время как Pochang и Chris верны, что для исправления этой проблемы в Safari необходим заголовок Content-Range, для Chrome требуется один дополнительный заголовок, который должен быть включен для правильной работы currentTime:

header( 'Accept-Ranges: bytes');

Обратите внимание, что вам действительно не нужно правильно отвечать на заголовок Range запроса, вам просто нужно включить его в ответ.

Ответ 3

У меня такая же проблема. Ключевым моментом является заголовок Content-Range. Все работает отлично после того, как я добавлю его в mp3-выход php.

Ответ 4

Почан правильный. Включение заголовка Content-Range в ответе PHP заставит Safari вести себя корректно. Он также позволяет искать (media.currentTime = 0;) без страшного INDEX_SIZE_ERR в Safari, но не в Chrome.

Код PHP для заголовка:

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