如何通过PHP结合FFMPEG实现视频流实时加水印等处理并输出到HTML5播放器
实现方案
核心原理
这个需求完全可以通过原生ffmpeg命令实现,不需要落地临时文件。核心是利用ffmpeg支持从stdin读取输入流、向stdout输出处理后流的特性,和PHP的进程管道做对接即可,不用单独把读取到的片段变量做额外转存。
具体实现步骤
替换原有手动读取文件输出的逻辑,改用proc_open启动ffmpeg进程,绑定IO管道直接对接流输入输出,参考代码如下:
// 替换为你自己的水印文件绝对路径 $watermarkPath = '/var/www/static/watermark.png'; // 你的原始视频文件路径,和原有逻辑中的视频路径变量对应即可 $originVideoPath = $this->filePath; // 适配实时流式输出的ffmpeg命令 $ffmpegCmd = [ 'ffmpeg', '-i', $originVideoPath, '-i', $watermarkPath, // 水印规则示例:左下角留10px边距,可自行调整位置、透明度等参数 '-filter_complex', 'overlay=10:main_h-overlay_h-10', '-c:a', 'copy', // 音频直接拷贝不转码,降低性能消耗 '-c:v', 'libx264', // 关键配置:让MP4支持流式输出,不需要等全部处理完成再输出元数据 '-movflags', 'frag_keyframe+empty_moov', '-f', 'mp4', '-' // 处理结果直接输出到stdout ]; // 进程管道配置 $descriptorspec = [ 0 => ['pipe', 'r'], // 标准输入 1 => ['pipe', 'w'], // 标准输出,读取处理后的视频流 2 => ['pipe', 'w'] // 标准错误,用于排查执行问题 ]; // 启动ffmpeg进程 $process = proc_open(implode(' ', $ffmpegCmd), $descriptorspec, $pipes); if (!is_resource($process)) { throw new \Exception('ffmpeg进程启动失败'); } // 直接传文件路径的场景不需要写入stdin,直接关闭即可 fclose($pipes[0]); stream_set_blocking($pipes[1], false); // 流式输出处理后的内容到前端 while (!feof($pipes[1])) { echo fread($pipes[1], 8192); flush(); ob_flush(); } // 回收资源 fclose($pipes[1]); $errLog = stream_get_contents($pipes[2]); fclose($pipes[2]); proc_close($process);
注意事项
- 兼容断点续传:如果你的场景需要适配HTML5播放器的
Range断点续传请求,需要在启动ffmpeg之前添加-ss参数指定开始时间、-t参数指定片段时长,和你原有逻辑里解析Range头得到的偏移对应即可。 - 性能消耗:实时转码加水印对服务器CPU性能要求较高,高并发场景建议优先选择提前对视频做离线水印处理,仅特殊场景使用实时处理方案。
- 水印自定义:可以根据需求调整水印参数,比如透明度、位置、动态水印等,仅需要修改
filter_complex对应的滤镜规则即可。 - 变量输入适配:如果确实需要把你读取到的
$data变量传给ffmpeg处理,只需要把ffmpeg命令的输入参数改成-,然后循环把$data写入进程的stdin管道即可,不过直接给ffmpeg传原始视频文件路径性能更高,不需要PHP层面做额外的文件读取。
内容的提问来源于stack exchange,提问作者mehra
相关产品推荐
相关产品推荐

