HTML5 <аудио> Прямая трансляция Safari против не
Я пытаюсь встроить аудиоэлемент HTML5, указывающий на данные MP3 или OGG, обслуживаемые файлом PHP. Когда я просматриваю страницу в Safari, появляются элементы управления, но пользовательский интерфейс говорит: "Прямая трансляция". Когда я нажимаю кнопку воспроизведения, звук начинается, как и ожидалось. Однако, как только он закончится, я не могу начать воспроизведение снова, нажав кнопку воспроизведения. Даже использование JS API для аудиоэлемента и установка значения currentTime в 0 завершается с ошибкой индексации.
Я подозревал, что проблема заключается в заголовках из скрипта PHP, в частности, в нем отсутствует длина контента. Но это не так. Заголовки ответа включают в себя соответствующую длину содержимого, чтобы указать, что аудио имеет конечный размер. Кроме того, в Firefox 3.5+ все работает как положено. Я могу нажать кнопку воспроизведения на аудиоэлементе несколько раз, чтобы услышать воспроизведение звука.
Если я уберу скрипт PHP из уравнения и предоставлю статическую копию файла MP3, в Safari все будет работать нормально.
Означает ли это, что Safari обрабатывает 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:
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 в скрипте, но безуспешно.
4 ответа
Можете ли вы опубликовать заголовки, отправленные сервером как с PHP-скриптом, так и без него? Мне интересно, если скрипт 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 /stackru/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1 Хост: ephemera.continuation.org Диапазон: байты =0-1 Подключение: закрыть Пользователь-агент: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Accept: */* Accept-Encoding: identity Cookie: [отредактировано] HTTP/1.1 206 Частичное содержимое Дата: вторник, 05 января 2010 02:12:48 GMT Сервер: Apache Последнее изменение: вторник, 05 янв. 2010 02:02:08 GMT ETag: "b2a80ad-11f6-47c6139aaa800" Диапазон принятия: байты Длина контента: 2 Диапазон контента: байты 0-1/4598 Соединение: закрыть Тип контента: аудио /mpeg # 2 байта из data GET /stackru/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1 Хост: ephemera.continuation.org Диапазон: байты =0-4597 Соединение: закрыть User-Agent: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Принять: * / * Принять-Кодирование: удостоверение Cookie: [отредактировано] HTTP/1.1 206 Частичное содержимое Дата: вторник, 05 января 2010 02:12:48 GMT Сервер: Apache Последнее изменение: вторник, 05 Январь 2010 02:02:08 GMT ETag: "b2a80ad-11f6-47c6139aaa800" диапазоны принятия: байты длина содержимого: 4598 Content-Range: байты 0-4597/4598 Соединение: закрыть Тип контента: audio/mpeg # 4598 байтов данных GET /stackru/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1 Хост: ephemera. continueation.org Диапазон: байты =0-1 Соединение: закрыть Пользователь-агент: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Принять: * / * Принять-Кодирование: удостоверение Cookie: [отредактировано] If-None-Match: "b2a80ad-11f6-47c6139aaa800" If-Modified-Since: вторник, 05 января 2010 02:02:08 GMT HTTP/1.1 304 Дата изменения: вторник, 05 января 2010 02:12:49 GMT Сервер: Apache Подключение: закрыть ETag: "b2a80ad-11f6-47c6139aaa800" # нет данных GET /stackru/audio-test/say-noid3?foo=bar&format=.mp3 HTTP/1.1 Хост: ephemera.continuation.org Диапазон: байты =1158-4597 Подключение: закрыть Пользователь-агент: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Принять: * / * Принять-Кодирование: удостоверение Cookie: [отредактировано] HTTP/1.1 206 Частичное содержимое Дата: вт, 05 янв 2010 02:12:49 GMT Сервер: Apache Последнее изменение: вторник, 05 января 2010 г. 02:02:08 GMT ETag: "b2a80ad-11f6-47c6139aaa800" Accept-Ranges: байты Content-Length: 3440 Content-Range: байты 1158-4597/4598 Соединение: закрыть Content-Type: audio/mpeg # 3440 байтов данных
Во всяком случае, о том, как он работает с выводом вашего сценария PHP. Здесь Safari снова пытается загрузить первые два байта, но ваш скрипт игнорирует Range
запрос и возвращает все это. Видимо, WebKit это не нравится, и поэтому он пытается снова, без Range
запрос. Опять же, ваш скрипт отправляет все содержимое. Safari теперь пытается еще раз, добавив Icy-Metadata
заголовок, который указывает, что он думает, что он загружает поток и хочет отправить потоковые метаданные. Наконец, он принимает вывод этого, и <audio>
Элемент может играть.
GET /say.php?text=this%20is%20a%20test&format=.mp3 HTTP/1.1 Ведущий: tts.mindtrove.info Диапазон: байты =0-1 Подключение: закрыть Пользователь-агент: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Принять: * / * Accept-Encoding: личность HTTP/1.1 200 ОК Дата: вторник, 05 января 2010 г. 02:14:28 GMT Сервер: Apache X-Powered-By: PHP/5.2.10 Длина контента: 4598 Подключение: закрыть Тип контента: аудио / MPEG # 4598 байт данных GET /say.php?text=this%20is%20a%20test&format=.mp3 HTTP/1.1 Ведущий: tts.mindtrove.info Подключение: закрыть Пользователь-агент: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Принять: */* HTTP/1.1 200 ОК Дата: вторник, 05 января 2010 г. 02:14:28 GMT Сервер: Apache X-Powered-By: PHP/5.2.10 Длина контента: 4598 Подключение: закрыть Тип контента: аудио / MPEG # 4598 байт данных GET /say.php?text=this%20is%20a%20test&format=.mp3 HTTP/1.1 Ведущий: tts.mindtrove.info Принять: * / * Пользователь-агент: Apple Mac OS X v10.6.2 CoreMedia v1.0.0.10C540 Ледяные метаданные: 1 Подключение: закрыть HTTP/1.1 200 ОК Дата: вторник, 05 января 2010 г. 02:14:28 GMT Сервер: Apache X-Powered-By: PHP/5.2.10 Длина контента: 4598 Подключение: закрыть Тип контента: аудио / MPEG # 4598 байт данных
Таким образом, кажется, что Safari (или, точнее, QuickTime, который Safari использует для обработки всех медиафайлов и загрузки медиафайлов) имеет совершенно запутанный подход к загрузке медиафайлов. Что-то в том, как вы отправляете свои данные обратно, вероятно, тот факт, что вы не отвечаете на Range
запросы, заставляет его думать, что вы отправляете потоковое мультимедиа, вызывая его повторную загрузку контента (хотя даже при столкновении с сервером, который реагирует на Range
запрос, он по-прежнему выполняет несколько запросов больше, чем нужно).
Я бы посоветовал попытаться ответить соответствующим образом на Range
Запросы; при подаче мультимедиа браузеры, скорее всего, будут использовать их, чтобы попытаться минимизировать пропускную способность, только буферизуя столько, сколько им необходимо для воспроизведения (хотя autobuffer
атрибут, который указывает, что вы хотели бы, чтобы они целиком буферизовали, браузеры могут игнорировать это). Я бы порекомендовал использовать X-Sendfile
разрешить Apache обрабатывать запросы к файлам, кешированию и диапазонам, но вы, похоже, находитесь на Dreamhost, который не имеет mod_xsendfile
установлен, так что вам придется свернуть свой собственный Range
обработки.
Старая тема, но все еще актуальна в 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);
Для записи, хотя и Pochang, и Chris правы, что вам нужен заголовок Content-Range для решения этой проблемы в Safari, Chrome требуется один дополнительный заголовок, который должен быть включен для правильной работы currentTime:
header( 'Accept-Ranges: bytes');
Обратите внимание, что на самом деле вам не нужно правильно отвечать на заголовок Range запроса, вы просто должны включить это в ответ.
Почан это правильно. Включение заголовка Content-Range в ответ PHP приведет к тому, что Safari будет вести себя правильно. Это также позволяет выполнять поиск (media.currentTime = 0;) без страшного INDEX_SIZE_ERR в Safari, но не в Chrome.
Код PHP для заголовка:
$len = strlen( $data );
$shortlen = $len - 1;
header( 'Content-Range: bytes 0-'.$shortlen.'/'.$len);
У меня та же проблема. Ключевым моментом является заголовок Content-Range. Все отлично работает после того, как я добавляю его в mp3-выход php.
И в 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;
Исходный вопрос здесь: проблема с синхронизацией для сгенерированного звука в некоторых браузерах