PHP项目会议录制文件如何存储转码管理

wen PHP项目 30

本文目录导读:

PHP项目会议录制文件如何存储转码管理

  1. 整体架构设计
  2. 存储方案
  3. 转码方案
  4. 异步任务队列管理
  5. 管理后台功能
  6. 播放器集成
  7. 关键注意事项
  8. 推荐技术栈(生产级)

针对PHP项目中的会议录制文件存储、转码与管理,这是一个典型的视频处理与媒资管理场景,下面从架构设计、技术选型到代码实现,提供一套完整的解决方案。


整体架构设计

录制文件上传 → 存储系统 → 异步转码队列 → 转码处理 → 转码结果存储 → 播放/管理

核心组件:

  • 存储层:对象存储(OSS/S3)或本地NAS
  • 转码引擎:FFmpeg(命令行工具或PHP扩展)
  • 队列系统:Redis + PHP队列(如 Laravel Queue / 自定义)
  • 数据库:MySQL/PostgreSQL 记录元数据与转码状态
  • 播放器:Video.js / HLS.js(支持HLS流)

存储方案

推荐:对象存储(阿里云OSS / AWS S3 / MinIO)

// 使用阿里云OSS SDK示例
use OSS\OssClient;
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
// 上传原始录制文件
$ossClient->uploadFile($bucket, 'recordings/2025/04/original/meeting_123.mp4', $localFilePath);

优势:弹性扩容、CDN加速、支持分片上传大文件。

本地存储(小规模场景)

$basePath = '/data/recordings/';
$datePath = date('Y/m/d');
$targetPath = $basePath . $datePath . '/' . $fileName;
move_uploaded_file($_FILES['file']['tmp_name'], $targetPath);

注意:需要配置Nginx/Apache的访问权限与防盗链。

文件命名规范

function generateFileName($meetingId, $extension = 'mp4') {
    $timestamp = time();
    $uuid = uniqid();
    return sprintf('%s_%s_%s.%s', $meetingId, $timestamp, $uuid, $extension);
}

转码方案

转码目标(推荐输出格式)

用途 格式 说明
播放 HLS (.m3u8) 自适应码率,支持分段加载
播放 MP4 兼容老设备
缩略图 JPEG/PNG 视频封面
音频 MP3/AAC 语音回放

使用 FFmpeg 转码(核心)

class VideoTranscoder {
    private $ffmpegPath = '/usr/bin/ffmpeg';
    public function transcodeToHLS($inputFile, $outputDir) {
        $cmd = sprintf(
            '%s -i %s -c:v libx264 -c:a aac -strict -2 -f hls -hls_time 10 -hls_list_size 0 -hls_segment_filename %s/segment_%%03d.ts %s/playlist.m3u8',
            $this->ffmpegPath,
            escapeshellarg($inputFile),
            escapeshellarg($outputDir),
            escapeshellarg($outputDir)
        );
        exec($cmd, $output, $returnCode);
        return $returnCode === 0;
    }
    public function generateThumbnail($inputFile, $outputFile, $time = '00:00:05') {
        $cmd = sprintf(
            '%s -i %s -ss %s -vframes 1 -vf scale=320:-1 %s',
            $this->ffmpegPath,
            escapeshellarg($inputFile),
            $time,
            escapeshellarg($outputFile)
        );
        exec($cmd, $output, $returnCode);
        return $returnCode === 0;
    }
}

多码率自适应转码(高级)

public function transcodeAdaptive($inputFile, $outputBase) {
    // 生成 360p, 720p, 1080p
    $profiles = [
        ['width' => 640, 'height' => 360, 'bitrate' => '800k', 'suffix' => '360p'],
        ['width' => 1280, 'height' => 720, 'bitrate' => '2500k', 'suffix' => '720p'],
        ['width' => 1920, 'height' => 1080, 'bitrate' => '5000k', 'suffix' => '1080p'],
    ];
    foreach ($profiles as $p) {
        // 每个分辨率生成独立的HLS目录
    }
    // 最后合并生成主playlist.m3u8
}

异步任务队列管理

数据库表结构设计

CREATE TABLE recordings (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    meeting_id VARCHAR(64) NOT NULL,
    original_filename VARCHAR(255),
    original_path VARCHAR(500),       -- 原始文件路径/OSS key
    file_size BIGINT,
    duration INT,                     -- 视频时长(秒)
    status ENUM('pending', 'uploaded', 'transcoding', 'completed', 'failed') DEFAULT 'pending',
    hls_path VARCHAR(500),            -- HLS播放地址
    thumbnail_path VARCHAR(500),      -- 缩略图路径
    created_at DATETIME,
    updated_at DATETIME
);
CREATE TABLE transcoding_jobs (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    recording_id BIGINT,
    job_type ENUM('hls', 'thumbnail', 'audio'),
    status ENUM('queued', 'processing', 'completed', 'failed'),
    output_path VARCHAR(500),
    error_message TEXT,
    created_at DATETIME,
    completed_at DATETIME
);

队列处理流程(基于Redis)

// 1. 上传完成后,将转码任务推入队列
$redis->lpush('transcode:queue', json_encode([
    'recording_id' => 123,
    'input_path' => '/path/to/original.mp4',
    'output_dir' => '/data/hls/meeting_123/'
]));
// 2. Worker进程从队列中取出并执行
while ($job = $redis->rpop('transcode:queue')) {
    $data = json_decode($job, true);
    $transcoder = new VideoTranscoder();
    $result = $transcoder->transcodeToHLS($data['input_path'], $data['output_dir']);
    // 更新数据库状态
    updateRecordingStatus($data['recording_id'], $result ? 'completed' : 'failed');
}

管理后台功能

文件管理界面(示例逻辑)

// 获取录制列表
$recordings = DB::table('recordings')
    ->where('meeting_id', $meetingId)
    ->orderBy('created_at', 'desc')
    ->get();
// 显示播放/下载/删除操作
foreach ($recordings as $rec) {
    echo '<a href="' . $rec->hls_path . '">播放</a>';
    echo '<a href="/download?id=' . $rec->id . '">下载</a>';
    echo '<button onclick="deleteRecording(' . $rec->id . ')">删除</button>';
}
// 删除时,需同时清理OSS/本地文件
function deleteRecording($id) {
    $rec = DB::table('recordings')->find($id);
    // 删除原始文件
    Storage::delete($rec->original_path);
    // 删除HLS目录
    Storage::deleteDirectory(dirname($rec->hls_path));
    // 删除数据库记录
    DB::table('recordings')->delete($id);
}

状态监控与重试机制

// 检测失败任务
$failedJobs = DB::table('transcoding_jobs')
    ->where('status', 'failed')
    ->where('created_at', '>', now()->subHours(1))
    ->get();
foreach ($failedJobs as $job) {
    // 重试逻辑:最多重试3次
    if ($job->retry_count < 3) {
        $redis->lpush('transcode:queue', $job->payload);
        DB::table('transcoding_jobs')
            ->where('id', $job->id)
            ->increment('retry_count');
    }
}

播放器集成

<!-- HLS播放器示例 -->
<link href="https://cdn.jsdelivr.net/npm/video.js@7/dist/video-js.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/video.js@7/dist/video.min.js"></script>
<video id="my-video" class="video-js vjs-default-skin" controls width="800" height="450">
    <source src="https://your-domain.com/recordings/meeting_123/playlist.m3u8" type="application/x-mpegURL">
</video>
<script>
    var player = videojs('my-video');
</script>

关键注意事项

问题 解决方案
大文件上传超时 使用分片上传(OSS分片 / 前端分块)
转码CPU/内存消耗 使用进程队列 + 限制并发数(如 Supervisor)
存储成本优化 设置生命周期规则:原始文件自动删除,保留HLS
播放卡顿 使用CDN加速 + 多码率自适应
安全性 播放地址加签(OSS防盗链 / 动态Token)

推荐技术栈(生产级)

  • 存储:阿里云OSS + CDN
  • 转码服务:阿里云/腾讯云媒体处理服务(代替自建FFmpeg)
  • 队列:Laravel Horizon / RabbitMQ
  • 框架:Laravel(自带Queue、Storage、ORM等)

如果预算有限,可以用 FFmpeg + Redis队列 + Nginx 搭建轻量方案,能满足中小规模(日均1000+会议)需求。

需要更具体的代码实现(如分片上传、转码进度回调等)可以进一步沟通。

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