ffmpeg不支持单命令“一进双出”原生并行编码,因webm与mp4容器及编解码器互不兼容,必须分别调用两次ffmpeg生成独立文件,并确保mp4含-movflags+faststart、webm用vp9/opus,再通过video标签按顺序引用。

PHP 本身不转视频,只调度 FFmpeg;WebM 和 MP4 是两种互不兼容的容器+编码组合,必须分别生成两个文件,不能“一个命令出双格式”。
为什么不能用单条 ffmpeg 命令同时输出 WebM 和 MP4
FFmpeg 不支持「一进双出」原生并行编码(即 -i input.mp4 同时写 output.mp4 和 output.webm)。虽然可以用 -map 和多个 -c:v 配合 tee muxer 实现,但实际中极易因时间基、关键帧对齐、音频重采样等差异导致播放异常,且调试成本高、失败率高。
更现实的做法是:一次输入,两次独立调用 ffmpeg,分别生成符合规范的 MP4(H.264 + AAC)和 WebM(VP9 + Opus 或 VP8 + Vorbis)。
- MP4 要求
moovatom 在文件开头(加-movflags +faststart) - WebM 无需 faststart,但推荐用
-dash 1或确保 keyframe interval ≤ 2s 以利流式加载 - 同一输入源下,两套命令的分辨率、帧率、关键帧间隔应尽量一致,否则前端
<video></video>切换时会跳帧或黑屏
exec() 分别生成 MP4 和 WebM 的最小可行命令
两条命令必须独立执行、分别检查返回码,并验证输出文件可读且非空。不要省略 escapeshellarg() ——路径含空格或中文时必崩。
生成 MP4:
$input = escapeshellarg('/tmp/src.avi');
$output_mp4 = escapeshellarg('/tmp/out.mp4');
$cmd_mp4 = "ffmpeg -y -i {$input} -vf 'scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2' -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart {$output_mp4} 2>&1";
exec($cmd_mp4, $mp4_output, $mp4_code);
if ($mp4_code !== 0) {
error_log('MP4 encode failed: ' . implode("\n", $mp4_output));
}
生成 WebM(VP9,兼顾兼容性与体积):
$output_webm = escapeshellarg('/tmp/out.webm');
$cmd_webm = "ffmpeg -y -i {$input} -vf 'scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2' -c:v libvpx-vp9 -crf 32 -b:v 0 -c:a libopus -b:a 96k {$output_webm} 2>&1";
exec($cmd_webm, $webm_output, $webm_code);
if ($webm_code !== 0) {
error_log('WebM encode failed: ' . implode("\n", $webm_output));
}
- VP9 比 VP8 更省带宽,现代浏览器(Chrome/Firefox/Edge ≥ 2023)均支持;Safari 仍不支持 WebM,所以 MP4 是 fallback 必选项
- 不用
-g显式设 GOP,依赖编码器默认(libx264 默认 250,libvpx-vp9 默认 ~240),只要输入帧率一致,输出关键帧间隔自然接近 - 务必加
-y,避免交互阻塞;2>&1抓全错误,仅靠返回码不足以判断是否真生成了可用文件
HTML video 标签如何正确引用双格式
浏览器按 <source></source> 顺序尝试,遇到第一个能解码的就停。MP4 放前面是稳妥选择,因为 Safari 完全不认 WebM。
<video controls width="100%"><source src="/uploads/video_abc.mp4" type="video/mp4"><source src="/uploads/video_abc.webm" type="video/webm"> Your browser does not support the video tag. </source></source></video>
- 确保 Web 服务器已配置
video/webmMIME 类型(Nginx 需在types块里加video/webm webm;;Apache 通常自带) - 两个文件名需严格对应(如仅扩展名不同),方便前端逻辑统一管理 URL
- 不要用 PHP 动态输出流来提供这两个文件——静态路径才能被浏览器缓存、拖拽、分片请求
容易被忽略的三个硬性检查点
生成完两个文件,不验证就上线,90% 会在某些设备上播失败。
- 用
ffprobe -v quiet -show_entries stream=codec_name,width,height,r_frame_rate -of default=nw=1:nk=1 {$file}确认 MP4 的视频流是h264、音频是aac;WebM 的视频流是vp9或vp8、音频是opus或vorbis - 用
stat -c "%s" {$file}或filesize()确保两个文件大小都 > 100KB;空文件或几 KB 的文件多半是编码中途崩溃了 - 在 iOS Safari、Chrome for Android、Firefox Desktop 上各手动点开一次,看能否拖动、暂停、快进——特别是 WebM 在 Safari 上必然空白,这是正常现象,不是 bug
双格式不是为了炫技,而是为覆盖所有主流环境;但每多一个格式,就多一倍的存储、校验和同步成本。如果业务 80% 流量来自微信内嵌浏览器(本质是 iOS Safari),那 WebM 可以直接砍掉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











