iOS Safari无法播放PHP代理分片MP4文件求助
解决iOS Safari中PHP代理MP4卡顿的问题
我之前也踩过iOS Safari处理视频分片请求的坑,你的代码里有几个关键问题导致了播放异常,咱们来一步步修复:
问题分析
- 字节范围计算错误:原代码中
$length = $size2 - $offset - 1;会导致返回的视频片段少一个字节,iOS对字节范围的校验极其严格,这种细微的错误会直接引发播放卡顿。 - Content-Length设置错误:在返回部分内容(206状态码)时,你仍然设置了整个文件的大小作为Content-Length,这会让Safari无法正确判断当前片段的长度。
- 重复处理HTTP_RANGE请求:代码中两次解析
$_SERVER['HTTP_RANGE'],不仅冗余还可能导致逻辑冲突。 - 响应头格式不严谨:部分响应头的格式不符合HTTP规范,比如Content-Range的结尾计算有误。
修改后的完整代码
<?php ini_set('max_execution_time', 0); $useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36"; $v = "http://192.168.xxx.xxx/bla..bla.mp4"; // 注意补全协议头 // 第一步:获取视频文件的基本信息 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $v); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_USERAGENT, $useragent); curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 合理设置超时,避免无限等待 $headerResponse = curl_exec($ch); $curlError = curl_errno($ch); if ($curlError !== 0) { http_response_code(500); echo "Curl Error: " . curl_error($ch); curl_close($ch); exit; } $filesize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD); curl_close($ch); // 处理Range请求 $offset = 0; $end = $filesize - 1; // 默认到文件最后一个字节 $isPartial = false; if (isset($_SERVER['HTTP_RANGE']) && preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches)) { $isPartial = true; $offset = intval($matches[1]); // 如果请求中指定了结束位置,就用指定的值,否则到文件末尾 $end = isset($matches[2]) ? intval($matches[2]) : $filesize - 1; // 确保结束位置不超过文件大小 $end = min($end, $filesize - 1); } $contentLength = $end - $offset + 1; // 计算实际返回的字节数 // 发送响应头 header("Content-Type: video/mp4"); header("Accept-Ranges: bytes"); if ($isPartial) { header("HTTP/1.1 206 Partial Content"); header("Content-Range: bytes {$offset}-{$end}/{$filesize}"); } header("Content-Length: {$contentLength}"); // 第二步:获取并输出指定范围的视频内容 $ch = curl_init(); $curlHeaders = []; if ($isPartial) { $curlHeaders[] = "Range: bytes={$offset}-{$end}"; } curl_setopt($ch, CURLOPT_URL, $v); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $curlHeaders); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_USERAGENT, $useragent); curl_setopt($ch, CURLOPT_NOBODY, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); // 直接输出到浏览器 curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_exec($ch); $curlError = curl_errno($ch); if ($curlError !== 0) { http_response_code(500); echo "Curl Error: " . curl_error($ch); } curl_close($ch); ?>
关键修改点说明
- 修复字节范围计算:正确计算
$end和$contentLength,确保返回的字节数完全符合HTTP Range请求的要求,这是iOS Safari能正常播放的核心。 - 修正Content-Length:在返回部分内容时,设置为当前片段的实际长度,而非整个文件大小。
- 简化Range请求处理:只解析一次
HTTP_RANGE,逻辑更清晰,避免冲突。 - 增加错误处理:添加curl错误检测,方便排查代理过程中的问题。
- 补全视频URL协议头:原代码中
$v的URL缺少http://或https://,这可能导致curl请求失败。
额外建议
- 确保源MP4文件本身是符合H.264基线或主 profile的编码,iOS对视频编码格式的兼容性比桌面端更严格。
- 如果视频文件很大,可以考虑添加输出缓冲控制,比如使用
ob_flush()和flush()来分段输出,但上面的代码中curl直接输出已经能满足需求。 - 测试时可以打开Safari的开发者工具(在设置中开启“Web检查器”),查看网络请求的响应状态码和头信息,确认206请求是否正确返回。
内容的提问来源于stack exchange,提问作者Laura Zyphy
相关产品推荐
相关产品推荐

