Ответ 1
Если вы сами так обрабатываете его, тогда вам также нужно будет обрабатывать запросы байтового диапазона.
Я использую PHP script для проверки запросов видео до их обслуживания. Этот script работает как на рабочем столе, так и в Safari и Chrome. Но на iOS я получаю сломанную кнопку воспроизведения.
Я уверен, что видео правильно закодировано для iPhone/iPad, потому что, когда я обращаюсь к нему напрямую, он работает так, как ожидалось.
Соответствующий PHP-код:
$file_name = 'test-video.mp4';
$file_size = (string)(filesize($file_name));
header('Content-Type: video/mp4');
header('Content-Length: '.$file_size);
readfile_chunked($file_name);
exit;
(readfile_chunked()
похож на readfile()
, но для очень больших файлов, найденных в комментариях на странице руководства PHP: http://php.net/manual/en/function.readfile.php В любом случае test-video.mp4
составляет всего ~ 5 МБ, что меньше предела памяти - и в этом случае я действительно могу заменить в нормальном readfile()
и произвести то же самое поведение.)
Заголовки, которые я получаю при прямом доступе к test-video.mp4
, следующие:
Accept-Ranges:bytes
Connection:Keep-Alive
Content-Length:5558749
Content-Type:video/mp4
Date:Sun, 27 Jun 2010 21:02:09 GMT
Etag:"1c04757-54d1dd-489944c5a6400"
Keep-Alive:timeout=10, max=30
Last-Modified:Tue, 22 Jun 2010 01:25:36 GMT
Server:Apache/2.2.15 (CentOS) mod_ssl/2.2.15 0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
Заголовки PHP script:
Connection:Keep-Alive
Content-Disposition:inline; filename="test-video.mp4"
Content-Length:5558749
Content-Type:video/mp4
Date:Sun, 27 Jun 2010 21:03:32 GMT
Keep-Alive:timeout=10, max=15
Server:Apache/2.2.15 (CentOS) mod_ssl/2.2.15 0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
X-Powered-By:PHP/5.2.13
Я пробовал много разных перестановок заголовков, даже точно их сопоставляя с запросами, безрезультатно.
Кто-нибудь успел обслуживать видео HTML5 через PHP, на iOS?
[Примечание. Я бы попытался использовать X-Sendfile, но сайт находится на общем хосте с очень ограниченным доступом.]
EDIT: Я читал, что iOS может быть чувствительным к расширениям файлов, поэтому я попытался настроить RewriteRule, который переписывает запросы MP4 обратно на мой оригинальный PHP script, но это не помогло либо.
Если вы сами так обрабатываете его, тогда вам также нужно будет обрабатывать запросы байтового диапазона.
Try:
$arquivo_caminho = 'path\file'
if (is_file($arquivo_caminho)){
header("Content-type: video/mp4"); // change mimetype
if (isset($_SERVER['HTTP_RANGE'])){ // do it for any device that supports byte-ranges not only iPhone
rangeDownload($arquivo_caminho);
} else {
header("Content-length: " . filesize($arquivo_caminho));
readfile($arquivo_caminho);
} // fim do if
} // fim do if
function rangeDownload($file){
$fp = @fopen($file, 'rb');
$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
// Now that we've gotten so far without errors we send the accept range header
/* At the moment we only support single ranges.
* Multiple ranges requires some more work to ensure it works correctly
* and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
*
* Multirange support annouces itself with:
* header('Accept-Ranges: bytes');
*
* Multirange content must be sent with multipart/byteranges mediatype,
* (mediatype = mimetype)
* as well as a boundry header to indicate the various chunks of data.
*/
header("Accept-Ranges: 0-$length");
// header('Accept-Ranges: bytes');
// multipart/byteranges
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
if (isset($_SERVER['HTTP_RANGE'])){
$c_start = $start;
$c_end = $end;
// Extract the range string
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
// Make sure the client hasn't sent us a multibyte range
if (strpos($range, ',') !== false){
// (?) Shoud this be issued here, or should the first
// range be used? Or should the header be ignored and
// we output the whole content?
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
// (?) Echo some info to the client?
exit;
} // fim do if
// If the range starts with an '-' we start from the beginning
// If not, we forward the file pointer
// And make sure to get the end byte if spesified
if ($range{0} == '-'){
// The n-number of the last bytes is requested
$c_start = $size - substr($range, 1);
} else {
$range = explode('-', $range);
$c_start = $range[0];
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
} // fim do if
/* Check the range and make sure it treated according to the specs.
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
*/
// End bytes can not be larger than $end.
$c_end = ($c_end > $end) ? $end : $c_end;
// Validate the requested range and return an error if it not correct.
if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size){
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
// (?) Echo some info to the client?
exit;
} // fim do if
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1; // Calculate new content length
fseek($fp, $start);
header('HTTP/1.1 206 Partial Content');
} // fim do if
// Notify the client the byte range we'll be outputting
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
// Start buffered download
$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end){
if ($p + $buffer > $end){
// In case we're only outputtin a chunk, make sure we don't
// read past the length
$buffer = $end - $p + 1;
} // fim do if
set_time_limit(0); // Reset time limit for big files
echo fread($fp, $buffer);
flush(); // Free up memory. Otherwise large files will trigger PHP memory limit.
} // fim do while
fclose($fp);
} // fim do function
Если вы читаете файл с http url, то вместо функции filsize() вы под кодом ниже для получения размера файла
function getFileSize($file) {
$ch = curl_init($file);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec($ch);
curl_close($ch);
$contentLength=0;
if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
// Contains file size in bytes
$contentLength = (int)$matches[1];
}
return $contentLength;
}
Обратите внимание, что это код (https://mobiforge.com/design-development/content-delivery-mobile-devices) - это спасатель. Однако будьте в поиске линии
"if ($ range {0} == '-') {" или "if ($ range0 == '-') {"
он должен быть
if ($ range [0] == '-') {
Эта опечатка привела к очень долгому выяснению, почему это не сработало.
У меня была проблема с этим кодом.
Fix:
set_time_limit(0); // Reset time limit for big files
ob_clean(); //added
echo fread($fp, $buffer);
flush(); // Free up memory. Otherwise large files will trigger PHP memory limit.
Как было сказано выше, для потоковой передачи или воспроизведения видеороликов MP4 с использованием PHP вам необходимо обрабатывать диапазоны байтов, если вы хотите правильно воспроизводить в Safari и iOS.
Функция rangeDownload()
, упомянутая в предыдущих ответах, выполняет работу довольно хорошо.
Я хочу упомянуть еще одну часть этой головоломки - убедитесь, что источник в видео заканчивается на .mp4
, как в <video source="url/yourfile.php/referenceForFile.mp4">
. Это делает браузером то, что это видеофайл, и он начинает рассматривать его как один.
Внутри yourfile.php
вы можете захватить входящую ссылку для вашего файла с помощью $_SERVER['PATH_INFO']
или внутри REQUEST_URI
. Не нужно передавать его как ?id=someId.mp4
, подход с прямой косой чертой больше похож на реальный файл.
Подводя итог, из моего опыта, чтобы правильно загрузить видеофайл с PHP, вам понадобится:
moov atom
в начале файла (вы можете использовать ffmpeg -movflags +faststart
или MP4Box
)<video source="...file.mp4">
Исходный атрибут тега видео должен выглядеть как файл .mp4
. Без этого мои видео воспроизводились только в Chrome, а не в Safari/iOS.videojs
Я написал это на основе моего опыта работы с тысячами видеороликов на моем веб-сайте музыкального видео. Хотя это может быть не для всех, но я нашел, что этот кросс-браузер и настройка кросс-устройств работают, как ожидалось.