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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 03:57:02