PHP项目直播录制与回放

wen PHP项目 3

本文目录导读:

PHP项目直播录制与回放

  1. 整体架构
  2. 核心组件选型
  3. PHP后端实现
  4. 前端播放实现
  5. 高级功能
  6. 部署建议
  7. 常见问题与解决方案

在PHP项目中实现直播录制与回放功能,通常需要结合流媒体服务器(如Nginx-RTMP、SRS、ZLMediaKit)和前端播放器(如Video.js、LivePlayer)来完成,以下是完整的实现方案和技术选型指南。

整体架构

直播推流 → 流媒体服务器 → 录制模块 → 文件存储 → PHP API → 前端播放
                 ↓
              实时转码

核心组件选型

流媒体服务器推荐

服务器 适合场景 特点
SRS 推荐首选 高性能、支持HLS/FLV/WebRTC,内置录制功能
Nginx + RTMP模块 轻量级需求 配置简单,但功能相对单一
ZLMediaKit 高并发场景 C++开发,性能极强

录制方案

服务器端自动录制(推荐)

使用SRS的dvr插件或Nginx-RTMP的录制指令:

SRS配置示例:

# srs.conf
listen              1935;
max_connections     1000;
vhost __defaultVhost__ {
    dvr {
        enabled     on;
        dvr_path    /usr/local/srs/record/[app]/[stream]/[2006]-[01]-[02]-[15].[05].[06].flv;
        dvr_plan    session;  # 每个推流生成一个文件
        dvr_duration    0;     # 0表示持续录制直到推流结束
        dvr_wait_keyframe on;
    }
    http_server {
        enabled         on;
        listen          8080;
        dir             ./objs/nginx/html;
    }
}

Nginx-RTMP录制配置:

rtmp {
    server {
        listen 1935;
        application live {
            live on;
            record all;
            record_path /var/record;
            record_unique on;
            record_suffix _%Y%m%d_%H%M%S.flv;
        }
    }
}

通过FFmpeg录制(灵活性高)

// PHP调用FFmpeg录制
$cmd = "ffmpeg -i rtmp://localhost/live/{stream_id} -c copy -f flv /var/record/{stream_id}_".time().".flv";
exec($cmd . " > /dev/null 2>&1 &");

PHP后端实现

数据库设计

CREATE TABLE `live_records` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `room_id` int(11) NOT NULL COMMENT '直播间ID',
    `stream_id` varchar(100) NOT NULL COMMENT '流标识', varchar(255) DEFAULT NULL COMMENT '录制标题',
    `start_time` datetime NOT NULL COMMENT '开始录制时间',
    `end_time` datetime DEFAULT NULL COMMENT '结束录制时间',
    `duration` int(11) DEFAULT '0' COMMENT '时长(秒)',
    `file_path` varchar(500) DEFAULT NULL COMMENT '录制文件路径',
    `status` tinyint(4) DEFAULT '0' COMMENT '0录制中 1已完成',
    `file_size` bigint(20) DEFAULT '0' COMMENT '文件大小(bytes)',
    `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_stream` (`stream_id`),
    KEY `idx_room` (`room_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

录制控制API

class RecordController {
    // 开始录制
    public function startRecord($roomId, $streamId) {
        // 调用系统命令或API通知录制服务器
        $cmd = "curl -X POST http://127.0.0.1:1985/api/v1/dvr/start -d '{
            \"vhost\": \"__defaultVhost__\",
            \"app\": \"live\",
            \"stream\": \"{$streamId}\"
        }'";
        $result = exec($cmd);
        // 记录到数据库
        DB::table('live_records')->insert([
            'room_id' => $roomId,
            'stream_id' => $streamId,
            'start_time' => date('Y-m-d H:i:s'),
            'status' => 0
        ]);
    }
    // 停止录制
    public function stopRecord($streamId) {
        $cmd = "curl -X POST http://127.0.0.1:1985/api/v1/dvr/stop -d '{
            \"vhost\": \"__defaultVhost__\",
            \"app\": \"live\",
            \"stream\": \"{$streamId}\"
        }'";
        exec($cmd);
        // 更新记录
        $record = DB::table('live_records')->where('stream_id', $streamId)
                    ->where('status', 0)->first();
        if ($record) {
            $endTime = date('Y-m-d H:i:s');
            DB::table('live_records')->where('id', $record->id)->update([
                'end_time' => $endTime,
                'duration' => strtotime($endTime) - strtotime($record->start_time),
                'status' => 1,
                'file_path' => "/record/{$streamId}.flv"
            ]);
        }
    }
}

回放列表API

// 获取回放列表
public function getRecordList($roomId, $page = 1, $limit = 20) {
    $list = DB::table('live_records')
        ->where('room_id', $roomId)
        ->where('status', 1)
        ->orderBy('start_time', 'desc')
        ->paginate($limit);
    return response()->json([
        'code' => 0,
        'data' => $list,
        'message' => 'success'
    ]);
}
// 获取单个录制详情
public function getRecordDetail($recordId) {
    $record = DB::table('live_records')->find($recordId);
    return response()->json([
        'code' => 0,
        'data' => [
            'id' => $record->id,
            'title' => $record->title,
            'start_time' => $record->start_time,
            'duration' => $this->formatDuration($record->duration),
            'file_url' => $this->getFileUrl($record->file_path),
            'status' => $record->status
        ]
    ]);
}

前端播放实现

播放器选择

<!-- 使用Video.js播放HLS -->
<link href="https://vjs.zencdn.net/7.20.3/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
<video id="record-player" class="video-js vjs-default-skin" controls preload="auto" width="640" height="360">
    <source src="https://your-domain.com/record/stream_id.m3u8" type="application/x-mpegURL">
</video>
<script>
var player = videojs('record-player');
</script>

直播回放点播

// 动态切换回放
function playRecord(recordId) {
    fetch(`/api/record/detail?id=${recordId}`)
        .then(response => response.json())
        .then(data => {
            if (data.code === 0) {
                player.src({
                    src: data.data.file_url,
                    type: 'application/x-mpegURL'
                });
                player.play();
            }
        });
}

高级功能

录制文件转码

// 使用FFmpeg将flv转mp4
public function convertToMp4($inputFile, $outputFile) {
    $cmd = "ffmpeg -i {$inputFile} -c copy -movflags +faststart {$outputFile} 2>&1";
    exec($cmd, $output, $returnVar);
    if ($returnVar === 0) {
        return true;
    }
    return false;
}

云端存储集成

// 使用阿里云OSS
use OSS\OssClient;
public function uploadToOss($localFile, $ossKey) {
    $client = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
    $result = $client->uploadFile($bucket, $ossKey, $localFile);
    return $result['oss-request-url'];
}

断点续传

// 分段上传
public function chunkUpload($file, $chunkIndex, $totalChunks) {
    $tempPath = storage_path('chunks/' . $file['unique_id']);
    file_put_contents($tempPath . '.' . $chunkIndex, file_get_contents($file['tmp_name']));
    if ($chunkIndex == $totalChunks - 1) {
        $finalFile = storage_path('records/' . $file['unique_id'] . '.mp4');
        for ($i = 0; $i < $totalChunks; $i++) {
            file_put_contents($finalFile, file_get_contents($tempPath . '.' . $i), FILE_APPEND);
            unlink($tempPath . '.' . $i);
        }
    }
}

部署建议

服务器配置优化

# Nginx配置
server {
    listen 80;
    server_name live.example.com;
    # 视频文件缓存
    location /record {
        root /var/www/records;
        expires 7d;
        add_header Cache-Control "public, immutable";
        # 支持断点续传
        add_header Accept-Ranges bytes;
    }
    # 直播流代理
    location /live {
        flv_live  on;
        chunked_transfer_encoding on;
        add_header 'Access-Control-Allow-Origin' '*';
        root /tmp;
    }
}

高性能传输

// 使用分片读取大文件
public function streamVideo($filePath) {
    header('Content-Type: video/mp4');
    header('Content-Length: ' . filesize($filePath));
    $fp = fopen($filePath, 'rb');
    while (!feof($fp)) {
        echo fread($fp, 8192);
        ob_flush();
        flush();
    }
    fclose($fp);
}

常见问题与解决方案

录制文件损坏

  • 原因: 推流异常中断
  • 解决: 启用dvr_wait_keyframe参数,使用ffmpeg修复

回放卡顿

  • 解决:
    • 使用HLS分片播放
    • 部署CDN加速
    • 调整dvr_duration为10秒分片

存储空间不足

  • 解决:
    • 自动清理规则
    • 使用对象存储OSS/COS
    • 设置录制时长限制

完整的PHP直播录制与回放方案需要以下组件配合:

  1. 流媒体服务器: SRS(推荐)或Nginx-RTMP
  2. 录制存储: 本地服务器或云端OSS
  3. PHP后端: 实现录制控制和回放管理
  4. 前端播放: Video.js或HLS.js
  5. 转码服务: FFmpeg转换格式
  6. CDN加速: 提升播放体验

对于中小型项目,推荐使用SRS + FFmpeg + PHP的组合,可快速实现高可用的直播录制与回放功能。

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