本文目录导读:

针对PHP处理直播流的问题,需要先明确一点:PHP本身并不适合直接处理高并发、低延迟的实时流媒体数据(如RTMP、HLS)。 PHP通常作为业务逻辑层和API网关,负责控制、调度、鉴权,而核心的流媒体转发、转码工作由专业的流媒体服务器(如Nginx-RTMP、SRS、FFmpeg)完成。
以下是PHP处理直播流的典型架构和实现方案:
核心架构:PHP vs 流媒体服务器
- 流媒体服务器(核心):负责接收推流、转码、分发。
- 常用:
Nginx + nginx-rtmp-module,SRS (Simple-RTMP-Server),Live555,Node-Media-Server
- 常用:
- PHP API服务(辅助):负责业务逻辑。
- 生成推流/播放地址(鉴权Token)。
- 管理直播间状态(创建、结束、禁播)。
- 处理回调(推流开始/结束、录制完成等)。
- 对接数据库和用户系统。
常见场景与PHP处理方式
场景1:生成和管理推流/播放地址(最常用)
这是PHP最擅长的领域,你不会让PHP直接推流,而是让它根据规则生成带有效期的URL,控制谁能推流、谁能观看。
流程:
- 用户请求创建直播间。
- PHP生成一个唯一的流名称(
StreamKey),并将其与AppName、用户ID、过期时间关联存入Redis/MySQL。 - PHP返回一个推流地址给主播端(如OBS):
rtmp://your-domain.com/live/{stream_key}?token={hash}。 - PHP返回一个播放地址给观众端(如Video.js):
http://your-domain.com/live/{stream_key}.flv或hls/stream_key.m3u8。
PHP代码示例(生成推流Token鉴权):
<?php
// 生成推流令牌,用于简单的防盗链
function generateStreamToken($userId, $streamKey, $expireTime) {
$secret = 'your_very_secret_key_123!'; // 与流媒体服务器配置的密钥一致
$data = $userId . $streamKey . $expireTime;
$hash = hash_hmac('sha256', $data, $secret);
return [
'push_url' => "rtmp://live.yourdomain.com/live/{$streamKey}?token={$hash}&expire={$expireTime}",
'play_url' => [
'flv' => "http://play.yourdomain.com/live/{$streamKey}.flv",
'hls' => "http://play.yourdomain.com/live/{$streamKey}.m3u8"
]
];
}
// 假设用户创建直播间
$userId = 12345;
$streamKey = uniqid('stream_', true); // 随机且唯一的流名
$expire = time() + 7200; // 2小时有效
$urls = generateStreamToken($userId, $streamKey, $expire);
echo json_encode($urls);
?>
关键配置(Nginx-RTMP):
# nginx.conf
rtmp {
server {
listen 1935;
application live {
live on;
record off;
# -- 鉴权(关键) --
# 当推流时,Nginx会请求PHP的API检查Token
on_publish http://your-api.com/auth/publish.php;
# 录制、转码等...
hls on;
hls_path /tmp/hls;
}
}
}
PHP鉴权回调脚本(auth/publish.php):
<?php
// 假设请求是这个:http://your-api.com/auth/publish.php?name=stream_key&token=xxx&expire=xxx
$streamKey = $_GET['name'] ?? '';
$token = $_GET['token'] ?? '';
$expire = $_GET['expire'] ?? 0;
// 1. 检查时间是否过期
if ($expire < time()) {
header('HTTP/1.0 403 Forbidden');
echo "Token expired";
exit;
}
// 2. 验证哈希
$secret = 'your_very_secret_key_123!';
$userInfo = getUserIdFromStreamKey($streamKey); // 从数据库获取
$expectedHash = hash_hmac('sha256', $userInfo['id'] . $streamKey . $expire, $secret);
if (!hash_equals($expectedHash, $token)) {
header('HTTP/1.0 403 Forbidden');
echo "Invalid token";
exit;
}
// 3. 验证成功,允许推流
header('HTTP/1.0 200 OK');
echo "Authenticated";
exit;
?>
场景2:监听流状态回调(推流/断流事件)
流媒体服务器(如Nginx-RTMP、SRS)支持在推流开始、结束、录制完成时,通过HTTP通知我们的PHP服务。
Nginx配置:
application live {
live on;
# 推流开始回调
on_publish http://your-api.com/callback/stream_start;
# 推流结束回调
on_publish_done http://your-api.com/callback/stream_end;
# 录制完成回调
on_record_done http://your-api.com/callback/record_done;
}
PHP回调处理(stream_end.php):
<?php
// 收到推流结束通知
$streamKey = $_POST['name'] ?? '';
$clientId = $_POST['clientid'] ?? '';
// 1. 更新数据库:将该直播间状态设为“已结束”或“离线”
$db->update('live_rooms', ['status' => 'offline', 'end_time' => date('Y-m-d H:i:s')], "stream_key = '$streamKey'");
// 2. 可以清理临时文件或发送WebSocket通知
// sendWebSocketNotification($streamKey, 'stream_ended');
echo "OK";
?>
场景3:处理录制文件(转码、存档、截图)
流媒体服务器可以自动录制直播流为FLV或MP4文件,PHP可以用于:
-
接收录制完成回调:处理并存储文件信息到数据库。
-
后台异步处理:使用
exec()或proc_open()调用FFmpeg进行视频截图、转码为小文件、生成水印等。<?php // 收到录制完成回调 $inputFile = '/path/to/recorded/stream-12345.flv'; $outputThumb = '/path/to/thumbs/stream-12345.jpg'; // 注意:主进程不要阻塞,建议使用消息队列 $cmd = "ffmpeg -i $inputFile -ss 00:00:10 -vframes 1 $outputThumb 2>&1"; exec($cmd, $output, $returnCode); if ($returnCode === 0) { // 保存截图路径到数据库 } ?>
重要限制与解决方案
| 限制 | 说明 | 解决方案 |
|---|---|---|
| 内存/时间 | PHP有max_execution_time和内存限制,不适合处理长时间流的转码。 |
使用exec()调用外部命令(FFmpeg),或用消息队列(RabbitMQ/Redis)异步处理。 |
| 并发 | PHP-FPM处理大量流媒体连接(如几千个观看者)会耗尽资源。 | 播放端不要直接连PHP,直接连Nginx/HLS/CDN,PHP只处理API请求。 |
| WebSocket/RTMP | PHP原生不支持低延迟的WebSocket或RTMP协议。 | 使用Swoole/Workerman(PHP常驻内存)或直接在流媒体服务器(Nginx-RTMP)配置。 |
推荐工作流程总结
主播(OBS)
|
| (推送RTMP流,带Token)
V
流媒体服务器 (Nginx-RTMP / SRS) <--- 推流回调通知 ---> PHP API服务
| (鉴权、记录状态、发通知)
| (自动HLS切片 / FLV分发)
V
CDN / 直接访问
|
| (HTTP-FLV / HLS)
V
观众 (Video.js / VLC)
实际生产建议
- 不要用PHP处理裸流数据:PHP的长连接、二进制数据处理能力弱。
- 使用成熟的流媒体方案:
- 推流:OBS、RTMP SDK(移动端直接用)。
- 服务器:推荐
SRS或Nginx-RTMP(社区版够用)。 - 播放器:
Vue/React使用Video.js或flv.js (HTTP-FLV)。
- PHP的定位:写好RESTful API,做好鉴权 (推流Token, 播放Referer/时间戳) 和回调 (Webhook)。
- 大并发考虑:在PHP和流媒体服务器之间加一层消息队列(如Kafka/RabbitMQ)来缓冲回调请求,防止高频回调打挂PHP。
如果你能提供更具体的场景(是搭建一个完整的直播系统,还是处理某个特定流?),我可以给出更精确的代码或配置示例。