I've encountered the issue with proc_open
on Windows, when trying to convert a wmv file (to flv), using ffmpeg
, however I suspect I'll encounter the same scenario whenever certain conditions occur.
Basically my code is as follows:
$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]));
Now, this code will cause PHP to hang indefinitely (it doesn't matter if instead of stream_get_contents
I'll use fgets
or stream_select
, the behavior is consistent).
The reason for it (I suspect) is that, while STDOUT stream is open succesfully, the process doesn't write anything to it (even though running the same command in cmd displays output) and as such, trying to read from such stream, would cause the same issue as described here, so - PHP waits for the stream to have anything in it, process doesn't write anything to it.
However (additional fun), setting stream_set_timeout
or stream_set_blocking
doesn't have any effect.
As such - can somebody confirm/deny on what is going on, and, if possible, show how can I cater for such situation? I've looked at PHP bugs, and all proc_open hangs
ones seem to be fixed.
For time being I've implemented such solution:
$timeout = 60;
while (true) {
sleep(1);
$status = proc_get_status($procedure);
if (!$status['running'] || $timeout == 0) break;
$timeout--;
}
However, I'd really not like to rely on something like this as:
Also, I don't really want to wait a full minute for the process to be checked (for example - converting the given video from command line takes <10s), and I'll have videos that take more time to be converted.
Per comment from @Sjon, here's stream_select
I was using, which blocks due to same issue - STDOUT not being written to:
$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;
Per conversation with @Sjon - reading from buffered streams on Windows is broken. The solution in the end is to use stream redirection via shell, and then read the created files - as such
$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");
As the stream is buffered, in the file we will get unbuffered output (something I was after as well). And we don't need to check if the file changes, because the result from shell is unbuffered and synchronous.
This took some time to reproduce, but I found your problem. The command you run, outputs some diagnostics when you run it; but it doesn't output to stdout, but to stderr. The reason for this is explained in man stderr
:
Under normal circumstances every UNIX program has three streams opened for it when it starts up, one for input, one for output, and one for printing diagnostic or error messages
If you would properly use streams; this wouldn't be an issue; but you call stream_get_contents($pipes[1])
instead. This results in PHP waiting for output from stdout, which never arrives. This fix is simple; read from stderr stream_get_contents($pipes[2])
instead and the script will quit immediately after the process ends
To expand on your addition of stream_select to the question; stream_select is not implemented on windows in php, it says so in the manual:
Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
So if the code posted above doesn't work; I'm not sure what will. Have you considered abandoning your streams solution, reverting to a simple exec()-call instead? If you append >%TEMP%/out.log 2>%TEMP%/err.log
to your command you can still read output from the process and it might finish quicker (without waiting for the unmodifiable timeout)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With