Я пытаюсь найти решение этой проблемы уже несколько дней, я пробовал все советы, которые я мог найти здесь, в stackoverflow и других платформах. И все же решения нет.
Я встраиваю видео через тег видео HTML5:
<video poster="thumb.png" controls="controls" preload="none" width="640" height="480"> <source src="provider.php?secure=12345" type="video/mp4"> </video>
Я пытаюсь доставить видеофайл MP4 через PHP, а не напрямую связывать его. Связывание файла mp4 напрямую работает и воспроизводит файл!
Тестирование:
Заметки:
Моя настройка:
.htaccess
в тестовой папке задает тип MIME и диапазоны Accept-Ranges:
AddType video/mp4 .mp4 <IfModule mod_headers.c> Header set Accept-Ranges "bytes" </IfModule>
Несмотря на то, что я создал один и тот же заголовок (сравнить тестовые URL-адреса 1. и 2. ), iPad не воспроизводит файл через запрос PHP.
Вместо этого я всегда получаю эту кнопку прокрутки:
Заголовки 1. (прямой вызов mp4) :
Заголовки 2. Те же заголовки, что и выше, но настроенные PHP (mp4, поставляемый PHP):
–
Я также пробовал прочитать весь видеофайл и отправить его в браузер с помощью PHP fread () , fpassthru () и file_get_contents (), но iPad всегда показывает значок can not-play-icon.
–
Мой размещенный сервер не поддерживает Connection keep-alive, может ли это быть проблемой? Является ли iPad интерпретацией .php отличным от .mp4?
Может ли кто-нибудь помочь мне избавиться от боли? Я полностью застрял.
PS: Что я пытался рассмотреть:
Я, наконец, получил MAC-адрес друга, связал iPad, открыл консоль отладки в Safari на Mac, загрузил страницу на iPad и проверил сообщения об ошибках, появляющиеся на Mac (кстати, насколько сложнее было бы заставить яблоко заставить нас развиваться …). Для всех тестовых скриптов эта ошибка появляется:
Failed to load resource: Plug-in handled load
Вау, это было сложно!
1. Первая серьезная проблема
Оказалось, что это не проблема с кодировкой, а проблема с заголовком контейнера mp4, установленным во время процесса преобразования видео. У iPad, очевидно, проблема с видеороликами MP4, которые подготовлены для прогрессивной потоковой передачи .
Сначала я обнаружил это в беседе здесь . После преобразования видео я всегда использовал инструмент MP4 Fast Start для подготовки видеофайла для прогрессивного потока. Это было необходимо для потоковой передачи видеофайла на Flash Player по частям (постепенно), поэтому он не загружал весь файл (и пользователю пришлось ждать).
С Handbrake существует аналогичная настройка, которая называется Web Optimized
. Он делает то же самое:
Web Optimized Also known as "Fast Start" This places the container header at the start of the file, optimizing it for streaming across the web.
Если вы включите это и конвертируете свое видео, iPad не будет воспроизводить видеофайл! Вместо этого вы получаете сообщение об ошибке «Операция не может быть завершена».
Проверьте и протестируйте его самостоятельно: ресурсы для тестирования видео .
2. Вторая проблема
В производственной среде я всегда использовал PHP для проверки реферирования. Как я узнал, iPad не отправляет информацию о реферере. Это также предотвращает потоковое вещание, и вы также увидите символ «не может играть» (значок с пропущенным воспроизведением).
3. Третья проблема
Я не мог понять, почему, но iPad принимает только потоковое видео из этого скрипта http://ideone.com/NPSlw5
<?php // disable zlib so that progress bar of player shows up correctly if(ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } $folder = '.'; $filename = 'video.mp4'; $path = $folder.'/'.$filename; // from: http://licson.net/post/stream-videos-php/ if (file_exists($path)) { // Clears the cache and prevent unwanted output ob_clean(); $mime = "video/mp4"; // The MIME type of the file, this should be replaced with your own. $size = filesize($path); // The size of the file // Send the content type header header('Content-type: ' . $mime); // Check if it's a HTTP range request if(isset($_SERVER['HTTP_RANGE'])){ // Parse the range header to get the byte offset $ranges = array_map( 'intval', // Parse the parts into integer explode( '-', // The range separator substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header ) ); // If the last range param is empty, it means the EOF (End of File) if(!$ranges[1]){ $ranges[1] = $size - 1; } // Send the appropriate headers header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges: bytes'); header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range // Send the ranges we offered header( sprintf( 'Content-Range: bytes %d-%d/%d', // The header format $ranges[0], // The start range $ranges[1], // The end range $size // Total size of the file ) ); // It's time to output the file $f = fopen($path, 'rb'); // Open the file in binary mode $chunkSize = 8192; // The size of each chunk to output // Seek to the requested start range fseek($f, $ranges[0]); // Start outputting the data while(true){ // Check if we have outputted all the data requested if(ftell($f) >= $ranges[1]){ break; } // Output the data echo fread($f, $chunkSize); // Flush the buffer immediately @ob_flush(); flush(); } } else { // It's not a range request, output the file anyway header('Content-Length: ' . $size); // Read the file @readfile($path); // and flush the buffer @ob_flush(); flush(); } } die(); ?>
в<?php // disable zlib so that progress bar of player shows up correctly if(ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } $folder = '.'; $filename = 'video.mp4'; $path = $folder.'/'.$filename; // from: http://licson.net/post/stream-videos-php/ if (file_exists($path)) { // Clears the cache and prevent unwanted output ob_clean(); $mime = "video/mp4"; // The MIME type of the file, this should be replaced with your own. $size = filesize($path); // The size of the file // Send the content type header header('Content-type: ' . $mime); // Check if it's a HTTP range request if(isset($_SERVER['HTTP_RANGE'])){ // Parse the range header to get the byte offset $ranges = array_map( 'intval', // Parse the parts into integer explode( '-', // The range separator substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header ) ); // If the last range param is empty, it means the EOF (End of File) if(!$ranges[1]){ $ranges[1] = $size - 1; } // Send the appropriate headers header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges: bytes'); header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range // Send the ranges we offered header( sprintf( 'Content-Range: bytes %d-%d/%d', // The header format $ranges[0], // The start range $ranges[1], // The end range $size // Total size of the file ) ); // It's time to output the file $f = fopen($path, 'rb'); // Open the file in binary mode $chunkSize = 8192; // The size of each chunk to output // Seek to the requested start range fseek($f, $ranges[0]); // Start outputting the data while(true){ // Check if we have outputted all the data requested if(ftell($f) >= $ranges[1]){ break; } // Output the data echo fread($f, $chunkSize); // Flush the buffer immediately @ob_flush(); flush(); } } else { // It's not a range request, output the file anyway header('Content-Length: ' . $size); // Read the file @readfile($path); // and flush the buffer @ob_flush(); flush(); } } die(); ?>
Надеюсь, эта информация поможет другим справиться с этой проблемой.
Обновление: три месяца спустя в производственной среде некоторые из моих пользователей по-прежнему сообщали о проблемах воспроизведения. Кажется, что еще одна проблема с Safari. Я посоветовал им использовать Chrome для iPad , это исправлено.
PS: Пару дней исследований и хлопот только для воспроизведения видеофайла, который, кстати, работает на всех других устройствах. Это снова доказывает мне, что Apple добилась успеха только из-за большого маркетинга, а не из-за большого программного обеспечения.