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

PHP输出的视频在iOS 12-13 Safari无法播放问题求助

iOS 12-13 Safari 无法播放PHP输出的MP4视频问题解决

问题场景

使用以下PHP类输出MP4视频文件时,多数浏览器(包括iOS15及以上版本的Safari)均可正常播放,但iOS12-13版本的Safari仅显示带叉号的播放图标,无控制台报错。直接访问视频文件的原始URL(如example.com/static/v/videotest.mp4)时,iOS12-13 Safari可正常播放,排除视频文件本身的问题。

原PHP代码:

class VideotestPage
{

    function __construct($params)
    {
        $videoFile = '../static/v/videotest.mp4';
        if (file_exists($videoFile)) {
            //Send the headers
            header('Content-Description: File Transfer');
            header('Content-Type: video/mp4');
            header('Content-Disposition: inline; filename="'.basename($videoFile).'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($videoFile));

            //Send file
            readfile($videoFile);
            exit;
        } else {
            echo "404 - $videoFile.";
        }
    }
}

问题根源

iOS12-13的Safari对视频流的播放有严格要求,必须支持范围请求(Range Requests)——浏览器会通过Range头请求视频的片段内容,而非一次性加载整个文件。原代码直接返回完整文件,未处理这类请求,导致旧版Safari无法解析视频流。此外,部分冗余的响应头也可能干扰浏览器的视频解析逻辑。

解决方案

修改PHP代码,添加范围请求处理逻辑,并调整响应头以适配旧版Safari:

class VideotestPage
{
    function __construct($params)
    {
        $videoFile = '../static/v/videotest.mp4';
        if (!file_exists($videoFile)) {
            http_response_code(404);
            echo "404 - $videoFile.";
            exit;
        }

        $fileSize = filesize($videoFile);
        $fileHandle = fopen($videoFile, 'rb');

        // 处理浏览器的范围请求
        $range = $_SERVER['HTTP_RANGE'] ?? '';
        if (preg_match('/bytes=(\d+)-(\d+)?/', $range, $matches)) {
            $start = intval($matches[1]);
            $end = isset($matches[2]) ? intval($matches[2]) : $fileSize - 1;
            $length = $end - $start + 1;

            // 返回部分内容的响应头
            http_response_code(206);
            header('Accept-Ranges: bytes');
            header('Content-Length: ' . $length);
            header("Content-Range: bytes $start-$end/$fileSize");
        } else {
            // 正常请求返回完整文件
            http_response_code(200);
            header('Content-Length: ' . $fileSize);
            header('Accept-Ranges: bytes');
        }

        // 设置视频播放必要的响应头
        header('Content-Type: video/mp4');
        header('Content-Disposition: inline; filename="' . basename($videoFile) . '"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');

        // 定位到请求起始位置并输出内容
        if (isset($start)) {
            fseek($fileHandle, $start);
        }
        fpassthru($fileHandle);
        fclose($fileHandle);
        exit;
    }
}

修改说明

  • 添加Accept-Ranges: bytes头,明确告知浏览器支持范围请求
  • 解析HTTP_RANGE头,处理部分内容请求,返回206 Partial Content状态码及对应内容范围
  • 使用fseek+fpassthru替代readfile,更高效地处理文件片段输出
  • 移除Content-Description: File Transfer头,避免干扰浏览器的视频解析逻辑
  • 提前判断文件是否存在,优化404响应逻辑

内容的提问来源于stack exchange,提问作者Maksim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:22:53