我在使用proc_open
尝试将wmv文件转换为flv时遇到了ffmpeg
在Windows上的问题,但我怀疑每当某些条件时我都会遇到相同的情况发生。
基本上我的代码如下:
$descriptorspec = array
(
array("pipe", "r"),
array("pipe", "w"),
array("pipe", "w")
);
$pipes = array();
$procedure = proc_open('cd "C:/Program Files/ffmpeg/bin" && "ffmpeg.exe" -i "C:/wamp/www/project/Wildlife.wmv" -deinterlace -qdiff 2 -ar 22050 "C:/wamp/www/project/Wildlife.flv"', $descriptorspec, $pipes);
var_dump(stream_get_contents($pipes[1]));
现在,这段代码会导致PHP无限期挂起(如果不是stream_get_contents
而是使用fgets
或stream_select
,则行为是一致的,这无关紧要。< / p>
它的原因(我怀疑)是,当STDOUT流成功打开时,该进程不会向其写入任何内容(即使在cmd显示输出中运行相同的命令),因此,尝试从这样的流会引起与here所描述的相同的问题,因此 - PHP等待流中包含任何内容,进程不会向其写入任何内容。
然而(额外的乐趣),设置stream_set_timeout
或stream_set_blocking
没有任何效果。
因此 - 有人可以确认/否认正在发生的事情,并且如果可能的话,表明我该如何迎合这种情况?我看过PHP错误,所有proc_open hangs
似乎都已修复。
暂时我实施了这样的解决方案:
$timeout = 60;
while (true) {
sleep(1);
$status = proc_get_status($procedure);
if (!$status['running'] || $timeout == 0) break;
$timeout--;
}
但是,我真的不想依赖这样的事情:
另外,我真的不想等待整整一分钟来检查进程(例如 - 从命令行转换给定的视频需要&lt; 10s),我会有更多时间的视频转换。
来自@Sjon的评论,这是{I}我正在使用的stream_select
,由于同样的问题阻塞了 - STDOUT没有被写入:
$descriptorspec = array
(
array("pipe", "r"),
array("pipe", "w"),
array("pipe", "w")
);
$pipes = array();
$procedure = proc_open('cd "C:/Program Files/ffmpeg/bin" && "ffmpeg.exe" -i "C:/wamp/www/sandbox/Wildlife.wmv" -deinterlace -qdiff 2 -ar 22050 "C:/wamp/www/sandbox/Wildlife.flv"', $descriptorspec, $pipes);
$read = array($pipes[0]);
$write = array($pipes[1], $pipes[2]);
$except = array();
while(true)
if(($num_changed_streams = stream_select($read, $write, $except, 10)) !== false)
{
foreach($write as $stream)
var_dump(stream_get_contents($stream));
exit;
}
else
break;
与@Sjon进行对话 - 从Windows上的缓冲流中读取内容已被破坏。最后的解决方案是通过shell使用流重定向,然后读取创建的文件 - 如此
$descriptorspec = array
(
array("pipe", "r"),
array("pipe", "w"),
array("pipe", "w")
);
$pipes = array();
$procedure = proc_open('cd "C:/Program Files/ffmpeg/bin" && "ffmpeg.exe" -i "C:/wamp/www/sandbox/Wildlife.mp4" -deinterlace -qdiff 2 -ar 22050 "C:/wamp/www/sandbox/Wildlife.flv" > C:/stdout.log 2> C:/stderr.log', $descriptorspec, $pipes);
proc_close($procedure);
$output = file_get_contents("C:/stdout.log");
$error = file_get_contents("C:/stderr.log");
unlink("C:/stdout.log");
unlink("C:/stderr.log");
当流被缓冲时,在文件中我们将得到无缓冲的输出(我之后也是如此)。我们不需要检查文件是否更改,因为shell的结果是无缓冲且同步的。
答案 0 :(得分:2)
这需要一些时间来重现,但我发现了你的问题。您运行的命令在运行时输出一些诊断信息;但它不会输出到stdout,而是输出到stderr。 man stderr
:
在正常情况下,每个UNIX程序在启动时都会为它打开三个流,一个用于输入,一个用于输出,另一个用于打印诊断或错误消息
如果你愿意properly use streams;这不是问题;但是你打电话给stream_get_contents($pipes[1])
。这导致PHP等待stdout的输出,它永远不会到来。这个修复很简单;从stderr stream_get_contents($pipes[2])
读取,脚本将在进程结束后立即退出
将您的stream_select添加到问题中;在php中的windows上没有实现stream_select,它在手册中也是这样说的:
对proc_open()返回的文件描述符使用stream_select()将失败并在Windows下返回FALSE。
所以如果上面发布的代码不起作用;我不确定会发生什么。您是否考虑过放弃您的流解决方案,转而使用简单的exec() - 调用?如果将>%TEMP%/out.log 2>%TEMP%/err.log
附加到命令中,您仍然可以读取进程的输出,并且可以更快地完成(无需等待不可修改的超时)