如何使用PHP Laravel将数据库存储的base64单帧转为类似YouTube的流式视频?
实现结论
完全可以实现,核心逻辑是先按顺序取出数据库中存储的base64帧、解码为原始图片二进制,再通过FFmpeg实时转码为支持流式播放的视频格式,最后通过Laravel的流式响应接口返回即可,播放体验和YouTube的流式加载逻辑一致。
前置依赖
- 服务器安装FFmpeg工具:Debian/Ubuntu执行
apt install ffmpeg即可,其他系统对应调整安装命令 - 确保PHP进程有权限调用FFmpeg、开启proc_open函数权限
- 存储帧的数据库表要有排序字段(如
frame_order),用来保证帧的播放顺序正确
代码实现
1. 路由定义
在routes/web.php中添加流式播放路由:
Route::get('/videos/{videoId}/stream', [VideoStreamController::class, 'stream']);
2. 控制器逻辑
新建app/Http/Controllers/VideoStreamController.php,核心逻辑如下:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class VideoStreamController extends Controller { public function stream($videoId) { // 调整PHP运行限制,避免转码超时/内存不足 ini_set('memory_limit', '512M'); set_time_limit(0); // 按顺序取出对应视频的所有帧base64 $frames = DB::table('video_frames') ->where('video_id', $videoId) ->orderBy('frame_order', 'asc') ->pluck('frame_base64'); // 定义FFmpeg命令:97帧10秒对应帧率为9.7,可按需调整为10fps,输出流式MP4 $ffmpegCmd = "ffmpeg -f image2pipe -r 9.7 -i - -c:v libx264 -pix_fmt yuv420p -movflags +faststart -f mp4 - 2>/dev/null"; // 打开FFmpeg进程管道 $descriptorspec = [ 0 => ["pipe", "r"], // 标准输入,用来写入帧二进制 1 => ["pipe", "w"], // 标准输出,用来读取转码后的视频内容 2 => ["pipe", "w"] // 标准错误 ]; $process = proc_open($ffmpegCmd, $descriptorspec, $pipes); if (!is_resource($process)) { abort(500, '转码进程启动失败'); } // 向FFmpeg输入流按顺序写入解码后的帧 foreach ($frames as $base64) { // 去除base64前缀(如果有data:image/xxx;base64,的头) $cleanBase64 = preg_replace('#^data:image/\w+;base64,#i', '', $base64); $frameBin = base64_decode($cleanBase64); fwrite($pipes[0], $frameBin); } fclose($pipes[0]); // 返回流式响应 return response()->stream(function() use ($pipes, $process) { while (!feof($pipes[1])) { echo fread($pipes[1], 1024 * 1024); ob_flush(); flush(); } fclose($pipes[1]); proc_close($process); }, 200, [ 'Content-Type' => 'video/mp4', 'Cache-Control' => 'public, max-age=31536000', 'Accept-Ranges' => 'bytes', // 支持断点续传、进度拖拽 ]); } }
优化建议
- 转码结果缓存:第一次转码完成后将视频文件存储到本地storage或对象存储,后续请求直接返回已转好的文件,避免重复转码消耗服务器资源
- 大流量场景下可以将转码逻辑异步化,上传帧的时候就触发队列转码,用户请求时直接返回已完成的视频流
- 如果需要实现YouTube同款多码率自适应播放,可以调整FFmpeg命令输出多码率HLS分片,前端配合hls.js实现自适应切换
- 长期存储建议归档base64帧数据,仅保留转码后的视频文件,降低数据库存储成本
内容的提问来源于stack exchange,提问作者Eslam
相关产品推荐
相关产品推荐

