You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 17:39:03