我试图找到这个问题的解决方案已经有几天了,我尝试了所有可以在stackoverflow和其他平台上找到的建议。但仍然没有解决方案。
我正在通过HTML5视频代码嵌入视频:
<video poster="thumb.png" controls="controls" preload="none" width="640" height="480">
<source src="provider.php?secure=12345" type="video/mp4">
</video>
我尝试通过PHP传送MP4视频文件,而不是直接链接它。链接mp4文件直接工作并播放文件!
测试:
注意:
我的设置:
test文件夹中的.htaccess
设置MIME类型和Accept-Ranges:
AddType video/mp4 .mp4
<IfModule mod_headers.c>
Header set Accept-Ranges "bytes"
</IfModule>
即使我创建了相同标题(比较测试网址1.和2.),iPad也没有通过PHP请求播放该文件。
相反,我总是得到这个严格的播放按钮:
2.的标题与上面相同的标题,但由PHP设置(由PHP提供的mp4):
-
我还尝试阅读整个视频文件并使用PHP fread(),fpassthru()和file_get_contents()将其发送到浏览器,但iPad始终显示无效 - 播放图标。
-
我的托管服务器不提供Connection keep-alive,这可能是个问题吗? iPad解释.php与.mp4不同吗?
有人可以帮我摆脱痛苦吗?我完全陷入困境。
PS:我试图考虑的事情:
我终于得到了朋友的MAC,连接了iPad,在Mac上的Safari中打开了调试控制台,在iPad上加载了页面并检查了Mac上出现的错误信息(顺便说一下,苹果强大了多么复杂?我们要发展......)。对于所有测试脚本,将出现此错误:
Failed to load resource: Plug-in handled load
答案 0 :(得分:14)
的 1。第一个主要问题
事实证明,没有编码问题,但在视频转换过程中设置的mp4容器标题存在问题 - iPad显然存在为渐进式流式传输准备的MP4视频的问题。
首先,我在对话here中发现了这一点。转换视频后,我总是使用工具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将无法播放视频文件!相反,您会收到错误“操作无法完成”。
自行检查并测试:video test resources。
的 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();
?>
我希望这些信息可以帮助其他人解决问题。
更新:三个月后,在生产环境中,我的一些用户仍然报告了播放问题。 Safari似乎还有另一个问题。我建议他们使用Chrome for iPad,修复它。
PS:几天的研究和麻烦只是播放一个视频文件,顺便说一句,它运行在所有其他设备上。这再一次向我证明,苹果公司因为出色的营销而获得了成功,而不是因为有很好的软件。
答案 1 :(得分:0)
感谢您的贡献,非常重要... 但是,即使您的代码也无法在我的iPhone上使用。
即使我仍然不知道为什么,下面的代码也对我有用。 可能是这样:
header('HTTP/1.1 416 Requested Range Not Satisfiable');
我从这里得到的: https://github.com/tikiatua/internal-assets-plugin/issues/9
$fp = fopen($filepath, "rb");
$size = filesize($filepath);
$length = $size;
$start = 0;
$end = $size - 1;
header('Content-type: video/mp4');
header("Accept-Ranges: 0-$length");
if (isset($_SERVER['HTTP_RANGE'])) {
$c_start = $start;
$c_end = $end;
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if (strpos($range, ',') !== false) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
exit;
}
if ($range == '-') {
$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;
}
$c_end = ($c_end > $end) ? $end : $c_end;
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");
exit;
}
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1;
fseek($fp, $start);
header('HTTP/1.1 206 Partial Content');
}
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: ".$length);
$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end) {
if ($p + $buffer > $end) {
$buffer = $end - $p + 1;
}
set_time_limit(0);
echo fread($fp, $buffer);
flush();
}
fclose($fp);
exit;
希望我帮助了某人! 欢呼大家
答案 2 :(得分:0)
(不幸的是,由于我是该论坛的新手,所以我无法发表评论,所以这是新帖子:)
关于#3脚本(第三个问题):它适用于所有浏览器,包括iOS上的Safari的较早版本,但不适用于iOS和MacOS的当前版本的Safari。
在第37行中将每个范围($ ranges [1])的末尾减少1:
else {
$ranges[1]--;
}
...并在第41行中将内容范围扩展1(如Konrad建议的那样):
header('Content-Length: ' . (($ranges[1] - $ranges[0]) + 1));
...为我工作,并解决了当前Safari版本的问题,尽管旧版本(例如,在iOS 9.3.5上)现在似乎不再起作用。