PHP流媒体点播怎么搞

wen PHP项目 5

本文目录导读:

PHP流媒体点播怎么搞

  1. 方案一:最简单的原生支持(伪流媒体 / HTTP Range)
  2. 方案二:使用 HLS 协议(推荐用于点播)
  3. 方案三:大文件 + 权限控制(受保护的多媒体)
  4. 关键性能建议

在 PHP 中实现流媒体点播(Video on Demand,简称 VOD),核心目标不是把整个视频文件一次性传给用户,而是支持拖拽(Seek)节省服务器带宽以及保护资源

这通常需要前端播放器(如 Video.js、HLS.js)与后端 PHP 配合,以下是几种由简到繁的实现方案:

最简单的原生支持(伪流媒体 / HTTP Range)

这是最基础的方式,PHP 不需要做太多事情,只需正确设置响应头,并支持 HTTP Range 请求(即告诉服务器客户端需要文件的哪一段)。

适用场景:MP4 文件(且编码为 H.264 + AACmoov 元数据在前),浏览器原生播放器即可播放并支持拖拽。

核心代码示例video.php):

<?php
$filePath = '/path/to/your/video.mp4'; // 这里是服务器上的实际路径
if (!file_exists($filePath)) {
    http_response_code(404);
    exit('File not found');
}
$fp = fopen($filePath, 'rb');
$size = filesize($filePath);
$length = $size; // 默认发送整个文件
$start = 0;
$end = $size - 1;
// 解析 Range 头
if (isset($_SERVER['HTTP_RANGE'])) {
    // 格式:bytes=start-end
    preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches);
    $start = intval($matches[1]);
    if (!empty($matches[2])) {
        $end = intval($matches[2]);
    }
    // 限制结束位置不能超过文件大小
    if ($end > $size - 1) $end = $size - 1;
    $length = $end - $start + 1;
    // 设置 206 状态码(Partial Content)
    http_response_code(206);
    header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
} else {
    http_response_code(200);
    header('Content-Length: ' . $size);
}
// 设置必要的响应头
header('Content-Type: video/mp4');
header('Accept-Ranges: bytes');
header('Content-Length: ' . $length);
// 移动指针并输出内容
fseek($fp, $start);
$chunkSize = 8192; // 8KB 块,防止一次性内存溢出
while ($length > 0 && !feof($fp)) {
    $read = min($chunkSize, $length);
    echo fread($fp, $read);
    $length -= $read;
    flush(); // 确保数据发送到客户端
}
fclose($fp);
exit;
?>

前端使用

<video controls src="video.php" style="width:100%"></video>

优点:实现简单,支持拖拽(取决于 MP4 是否包含 moov 元数据)。 缺点:如果视频很大,且 MP4 元数据在文件尾部,拖拽会非常慢(需下载整个文件才能解析),且不支持 HLS 加密。


使用 HLS 协议(推荐用于点播)

HLS(HTTP Live Streaming)是苹果推出的协议,现在基本是所有流媒体的标准,它将视频分割成很多 .ts(或 .m4s)切片,通过 .m3u8 索引文件进行播放。拖拽体验最好,支持最高码率自适应

注意:PHP 通常不负责视频切片(那是 FFmpeg 的活),而是负责提供切片文件动态生成播放列表

步骤 1:预处理视频(用 FFmpeg 切分)

在本地或服务器上执行:

# 将 input.mp4 转成 HLS 格式,生成 video.m3u8 和 多个 .ts 文件
ffmpeg -i input.mp4 -codec copy -hls_time 10 -hls_list_size 0 -hls_segment_filename public/media/segment_%03d.ts public/media/video.m3u8

注意:这一步通常不是 PHP 做的,而是在部署时用命令完成,或者通过 PHP 的 shell_exec 调用来完成(但需注意性能)。

步骤 2:PHP 提供支持(处理 .m3u8 和 .ts)

PHP 可以把这些文件放在公共目录,或者通过一个控制器来分发(用于权限控制)。

示例:player.php(返回播放列表)

<?php
// 假设你的 .m3u8 文件在 /var/www/html/media/video.m3u8
$playlistPath = __DIR__ . '/media/video.m3u8';
if (file_exists($playlistPath)) {
    header('Content-Type: application/vnd.apple.mpegurl'); // 标准 MIME 类型
    header('Cache-Control: no-cache');
    // 读取并输出内容,同时可能修改内部路径以适应你的 URL 规则
    $content = file_get_contents($playlistPath);
    // 如果内部是相对路径,一般无需修改,浏览器会基于当前 URL 解析
    echo $content;
} else {
    http_response_code(404);
}
?>

前端使用

<!-- 使用 hls.js 库(现代浏览器)+ HLS 原生支持(Safari) -->
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<video id="video" controls></video>
<script>
  var video = document.getElementById('video');
  var videoSrc = 'player.php'; // 你的 PHP 接口
  if (Hls.isSupported()) {
    var hls = new Hls();
    hls.loadSource(videoSrc);
    hls.attachMedia(video);
    hls.on(Hls.Events.MANIFEST_PARSED, function() {
      video.play();
    });
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    // 兼容 Safari
    video.src = videoSrc;
  }
</script>

优点:拖拽秒开、支持动态码率、安全(可配合 Token 认证)。 缺点:需要预先用 FFmpeg 切片,存储占用稍大。


大文件 + 权限控制(受保护的多媒体)

如果你不想文件直接在 public/media 目录下暴露(防止被盗链),可以放在 PHP 可以访问但外部无法直接访问的目录,通过 PHP 代理读取。

安全策略:签名 URL + 过期时间

<?php
// 生成播放链接(比如在用户点击播放时)
$videoId = 123;
// 生成一个 10 分钟有效的签名
$expires = time() + 600;
$token = md5($videoId . $expires . SECRET_KEY); // SECRET_KEY 是自定义常量
// 输出链接给前端
echo "play.php?id=$videoId&expires=$expires&token=$token";
?>

play.php 校验签名后再输出视频流(结合方案一或方案二)。


关键性能建议

  1. 开启 gzip不要开启 gzip!二进制视频流(MP4/TS)压缩率极低且浪费 CPU,应让 Nginx/Apache 跳过这些扩展名的压缩。

  2. 使用 X-Sendfile(强烈推荐):PHP 逐块读取并输出文件非常消耗内存和 CPU(每次请求都要跑 PHP 进程)。

    • Nginx 使用 X-Accel-Redirect
    • Apache 使用 mod_xsendfile
    • 原理:PHP 只负责验证权限和设置 Header,然后告诉 Nginx 返回这个文件,Nginx 直接读取磁盘文件,速度和性能最高。

    Nginx 示例(在 Nginx 配置中设置):

    location /protected_videos/ {
        internal; # 表示外部无法直接访问
        alias /var/www/files/;
    }

    PHP 代码

    <?php
    // 权限验证通过后
    header('Content-Type: video/mp4');
    header('X-Accel-Redirect: /protected_videos/' . $filename);
    // 不输出任何内容,直接结束
    exit;
    ?>
  3. APCu / Redis 缓存切片列表:如果你动态生成 .m3u8 播放列表,建议缓存几分钟,避免大并发时 MySQL/文件系统压力过大。

  • 普通 MP4(体积 < 2GB,不追求极致体验):用 方案一(Range + 原生 Video) 最快,但需注意前端加载速度,建议安装 moov 元数据前置工具(如 qt-faststart,或 FFmpeg 参数 -movflags +faststart)。
  • 企业级 / 大型点播:用 方案二(HLS + 切片),配合 CDN(内容分发网络),性能最稳。
  • 内存/性能优化:放弃 readfile()fread,改用 X-Sendfile 交给 Web 服务器处理。

抱歉,评论功能暂时关闭!