You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Safari无法播放PHP代理分片MP4文件求助

解决iOS Safari中PHP代理MP4卡顿的问题

我之前也踩过iOS Safari处理视频分片请求的坑,你的代码里有几个关键问题导致了播放异常,咱们来一步步修复:

问题分析

  1. 字节范围计算错误:原代码中$length = $size2 - $offset - 1;会导致返回的视频片段少一个字节,iOS对字节范围的校验极其严格,这种细微的错误会直接引发播放卡顿。
  2. Content-Length设置错误:在返回部分内容(206状态码)时,你仍然设置了整个文件的大小作为Content-Length,这会让Safari无法正确判断当前片段的长度。
  3. 重复处理HTTP_RANGE请求:代码中两次解析$_SERVER['HTTP_RANGE'],不仅冗余还可能导致逻辑冲突。
  4. 响应头格式不严谨:部分响应头的格式不符合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:14:34