MP4 проигрывается при прямом доступе, но не при чтении через PHP на iOS - PullRequest
17 голосов
/ 28 июня 2010

Я использую PHP-скрипт для проверки видео-запросов перед их обслуживанием. Этот скрипт работает, как и ожидалось, на рабочем столе с 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:

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, но сайт находится на общем хосте с очень ограниченным доступом.]

РЕДАКТИРОВАТЬ: Я читал, что iOS может быть чувствительна к расширениям файлов, поэтому я попытался настроить RewriteRule, который переписывает запросы MP4 обратно в мой оригинальный скрипт PHP, но это тоже не помогло.

Ответы [ 6 ]

15 голосов
/ 07 мая 2013

Попробуйте:

$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's 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's 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's memory limit.
        } // fim do while

        fclose($fp);
    } // fim do function
8 голосов
/ 17 июля 2010

Если вы обрабатываете это самостоятельно таким образом, то вам также придется обрабатывать запросы в диапазоне байтов.

0 голосов
/ 08 октября 2017

Как было сказано выше для потоковой передачи или воспроизведения 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.
  • Прямой проигрыватель HTML5, или вы можете использовать такую ​​библиотеку, как videojs

Я написал это на основемой опыт работы с тысячами видео на моем музыкальном веб-сайте.Хотя это может быть не для всех, но я обнаружил, что кросс-браузерная и кросс-браузерная установка работает, как и ожидалось.

0 голосов
/ 17 ноября 2015

У меня были проблемы с этим кодом.

Исправлено:

 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's memory limit.
0 голосов
/ 31 октября 2015

Обратите внимание, что это код (https://mobiforge.com/design-development/content-delivery-mobile-devices) - спасатель. Однако следите за строкой

"if ($ range {0} == '-') {" или "if ($ range0 == '-') {"

должно быть

if ($ range [0] == '-') {

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

0 голосов
/ 26 февраля 2014

Если вы читаете файл с http-адреса, тогда вместо функции 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...