Return of FFMPEG in Background - php

I am making a code that downloads a list of m3u8 links by FFMPEG
I had this code:
function FFMPEG($videocode, $dirvideo) {
$ffmpeg = '"D:\FFMPEG\bin\ffmpeg.exe"' . " -hide_banner -loglevel verbose -n -i https://linkplaylist/{$videocode}.m3u8 -map 0:1 -map 0:2 -acodec copy -bsf:a aac_adtstoasc -vcodec copy {$dirvideo} 1> log.txt 2>&1";
exec($ffmpeg, $output, $var);
return $var;
}
$code = FFMPEG('football', 'football.mp4');
if($code){
{ERROR CODE};
}else{
{SUCCESS CODE}
}
Initial problem
And that worked well. I could download the video and know if it was downloaded completely or had some error.
The problem is that this code "hangs" the script in exec () the page is loading until finalize exec () and that of timeout error (shared server) besides being visually strange to the visitor the page loading.
Resolution of the initial problem
After research I think the solution is to put the code execution in the background so I found this code:
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "error-output.txt", "a") // stderr is a file to write to
);
function FFMPEG($videocode, $dirvideo) {
$cmd = 'start /B D:\FFMPEG\bin\ffmpeg.exe -y -i "https://linkplaylist/{$videocode}.m3u8" -map p:0 -acodec copy -bsf:a aac_adtstoasc -vcodec copy {$dirvideo}';
proc_close(proc_open ($cmd
,$descriptorspec, $foo));
}
And finally my current problem
And this works fine for the loading and timeout issue, but I can not get a return from when the download was successfully completed.
1 ° proc_open Is this the best solution for my initial problem?
2 ° How can I get a return from when ffmpeg finishes running successfully and the script continues to flow.
Extra Info
I'm trying to build a solution that works on windows (xampp) but my final server is linux.

I'm having the same challenge, and hopefully someone can complete my contribution.
This is only a partial answer that I don't know how to complete for Windows. The trick is in the pipes apparently, per the ffmpeg wiki, but the solution is for Unix:
One more thing to take care about is the standard INPUT (stdin).
Command-line ffmpeg tool is designed as an interactive utility that
accepts user's input (usually from keyboard) and reports the error log
on the user's current screen/terminal. When we run ffmpeg in the
background, we want to tell ffmpeg that no input should be accepted
(nor waited for) from the stdin. We can tell this to ffmpeg, using I/O
redirection again "
I tried using the -nostdin, but it had no effect.

Related

ffmpeg command executes via command line, but not via PHP script

I am working on a simple video upload and compression system and I am currently using the following process.
First Step
I use a multipart/form-data to upload the original video file, which I store in /var/www/site/videos_pre/video.mp4. My public folder is var/www/site/public_html/. I store an entry in my database with the video information.
Second step
I have a converter process which is very basic, but does the job (at least in CLI).
It has the following code:
public function converter($content_id)
{
$content = $this->videos_m->get($content_id);
$id = uniqid();
$name_mp4 = $content->name_slug.'_'.$id.'.mp4';
$name_webm = $content->name_slug.'_'.$id.'.webm';
$command_mp4 = 'ffmpeg -i ../videos_pre/'.$content->original_file.' -b:v 1500k -bufsize 1500k ./videos/'.$name_mp4;
system($command_mp4);
$command_webm = 'ffmpeg -i ./videos/'.$name_mp4.' -c:v vp9 -c:a libvorbis ./videos/'.$name_webm;
system($command_webm);
$update_video = new stdClass();
$update_video->archivo_mp4 = $name_mp4;
$update_video->archivo_webm = $name_webm;
$this->db->where('content_id', $content_id);
$this->db->update('Videos', $update_video);
}
Third step - where the problem occurs
I have tested this on Windows 10 and Ubuntu 18.04. The code works on Windows 10 in both php and cli. The code on Ubuntu only works with cli.
The resulting commands are something like this:
// First command to reduce the bitrate of the mp4
ffmpeg -i /var/www/site/videos_pre/video.mp4 -b:v 1500k -bufsize 1500k /var/www/site/public_html/videos/video_5e757d3e0d762.mp4
// Second command to convert the mp4 to a webm to get both types
ffmpeg -i /var/www/site/public_html/videos/video_5e757d3e0d762.mp4 -c:v vp9 -c:a libvorbis /var/www/site/public_html/videos/video_5e757d3e0d762.webm
If I execute those commands they work perfectly. If I run the php script, be it in the browser or via CLI, literally nothing happens. No error messages nothing.
I am not sure if it's a permission issue, if there's something specific for php to run this, or some module I've not activated. I have enough execution time, I have it at 600 seconds, but as I said it doesn't even take time, it just does nothing.
I can even echo the commands and they appear. So, basically the problem is I need to be able to run those commands from PHP. I did it on Windows, but Ubuntu 18.04 won't let me. Both OSs have ffmpeg recently installed and I've been able to convert on both of them.
I tried changing paths from absolute to relative, no difference at all.
So, I managed to solve it, and it turns out it was a permission problem.
I had my /var/www/site folder with permissions like this: myuser:www-data. After checking this answer on a similar issue, I changed the folder permission to www-data:root and it worked instantly.
I hope that permission change doesn't affect the rest of things, but for now everything's working fine.

Run script after FFMPEG background task completes (via PHP)

I have a PHP script which triggers an FFMPEG file conversion via shell_exec().
shell_exec('ffmpeg -i file.webm -c:v libx264 -c:a aac -strict -2 file.mp4 >/dev/null 2>/dev/null &');
This happens in the background (hence &), i.e. the script completes before conversion has finished.
Is there a way to call and execute a PHP script (to update a DB flag) once the conversion is complete?
I've done plenty of Googling but my knowledge of server commands just isn't up to understanding what I'm reading (e.g. this answer). The best I could manage was to redirect stdout to a file via
shell_exec('ffmpeg -i file.webm -c:v libx264 -c:a aac -strict -2 file.mp4 > MYFILE.txt 2>/dev/null &');
...but obviously that just creates and writes to a file, it doesn't call and execute it via PHP.
I am not that great at server commands either, so I can't really help you there. But I do have this knack for figuring things out.
So I see a few ways you could do this, essentially you need PHP to do something when the command line call finishes. The obvious answer is to remove the & off the end of the command and make it blocking so PHP sticks around tell the job is done. But in doing so you can't return to the end user until that is done.
Option 1
So one way around this is to make a sort of Bootstrap PHP script that you call non-blocking. In this script do your now blocking conversion command and after that returns have PHP do something else.
//bootstrap.php
shell_exec('ffmpeg -i file.webm -c:v libx264 -c:a aac -strict -2 file.mp4 > MYFILE.txt 2>/dev/null'); //blocking
//Update the DB
Then from your Controller or what have you call the bootstrap non-blocking
shell_exec('php {pathto}/bootstrap.php 2>/dev/null &');
This way the call to the bootstrap returns immediately but the conversion call is blocking, which gives you the chance to update the DB afterwords.
Option 2
Because the conversion is outputting a file, you could start a separate background job, that monitors the modified time of the output file. Then if the modified time is like a minute in the past you could assume it's done converting and update the DB. The modified time should continue to update as long as data is being added to the file.
Hope that helps.
PS. I have some code you may fine useful on GitHub
Runs Background processes in both windows & linux
https://github.com/ArtisticPhoenix/MISC/blob/master/BgProcess.php
PHP process locking ( Mutex simulation using files)
https://github.com/ArtisticPhoenix/MISC/blob/master/ProcLock.php
Command line argument mapping for PHP programs:
https://github.com/ArtisticPhoenix/Cli
Your welcome to use them if it helps you out.

FFMpeg working in command line but not in PHP script

I am using FFMpeg to convert videos and it is working fine from the command line. I am using the following command:
ffmpeg -i input.avi -ab 56 -ar 44100 -b 200 -r 15 -s 320x240 -f flv output.flv
However, when I run the command using PHP script, the output video is not encoded.
exec("ffmpeg -i input.avi -ab 56 -ar 44100 -b 200 -r 15 -s 320x240 -f flv output.flv",$output, $returnvalue);
$returnvalue = 127;
FFMPEG installed path :
[root#localhost ~]# which ffmpeg
/root/bin/ffmpeg
My Script Path :
www.domainname.com/core/foldername/ffmpeg.php
Please provide me solution for the same ASAP.
Thank you.
The natural way to run ffmpeg from PHP scripts is something like:
<?php
echo "Starting ffmpeg...\n\n";
echo shell_exec("ffmpeg -i input.avi output.avi &");
echo "Done.\n";
?>
There are several issues that need to be pointed out here. The first one is that, although we specified we want ffmpeg to be executed in the background (using the ampersand operator "&"), PHP script will not continue it's execution until ffmpeg has finished its execution. This is due to the fact, mentioned in one of the notes for the ​PHP's exec() function that says:
If a program is started with this function, in order for it to
continue running in the background, the output of the program must be
redirected to a file or another output stream. Failing to do so will
cause PHP to hang until the execution of the program ends.
Don't be confused about the example showing ​shell_exec() call instead of ​exec(). All of the ​PHP's Program execution functions share the similar code base and limitations.
So, to work around this issue, we need to do something like this:
<?php
echo "Starting ffmpeg...\n\n";
echo shell_exec("ffmpeg -i input.avi output.avi >/dev/null 2>/dev/null &");
echo "Done.\n";
?>
The part that says ">/dev/null" will redirect the standard OUTPUT (stdout) of the ffmpeg instance to /dev/null (effectively ignoring the output) and "2>/dev/null" will redirect the standard ERROR (stderr) to /dev/null (effectively ignoring any error log messages). These two can be combined into a shorter representation: ">/dev/null 2>&1". If you like, you can ​read more about I/O Redirection.
An important note should be mentioned here. The ffmpeg command-line tool uses stderr for output of error log messages and stdout is reserved for possible use of pipes (to redirect the output media stream generated from ffmpeg to some other command line tool). That being said, if you run your ffmpeg in the background, you'll most probably want to redirect the stderr to a log file, to be able to check it later.
One more thing to take care about is the standard INPUT (stdin). Command-line ffmpeg tool is designed as an interactive utility that accepts user's input (usually from keyboard) and reports the error log on the user's current screen/terminal. When we run ffmpeg in the background, we want to tell ffmpeg that no input should be accepted (nor waited for) from the stdin. We can tell this to ffmpeg, using I/O redirection again ** "ffmpeg command-line tool in the background would be similar to this:
<?php
echo "Starting ffmpeg...\n\n";
echo shell_exec("ffmpeg -y -i input.avi output.avi </dev/null >/dev/null 2>/var/log/ffmpeg.log &");
echo "Done.\n";
?>
The ​"-y" option is used to auto-overwrite the output file (output.avi) without asking for yes/no confirmation. If you need the opposite scenario, to auto-cancel the entire process if the output file already exists, then use "-n" option instead.
Wrapper Libraries
Some PHP libraries allow wrapping ffmpeg calls into PHP objects, and give you a nice syntax to work with if you don't like to use the command line. One of these is the actively maintained ​PHP-FFMpeg. It only requires you to ​download a recent ffmpeg and ffprobe build apart from installing the PHP components. Then you can run PHP code like this:
$ffmpeg = FFMpeg\FFMpeg::create();
$video = $ffmpeg->open('video.mpg');
$video
->filters()
->resize(new FFMpeg\Coordinate\Dimension(320, 240))
->synchronize();
$video
->save(new FFMpeg\Format\Video\X264(), 'export-x264.mp4')
Of course you need to take care of running such a task in the background. Libraries such as ​GearmanClient facilitate this.
Note: ​ffmpeg-php is an extension that is not developed since 2007 (and requires "ffmpeg-0.4.9_pre1 or higher"), which means that you are restricted to use a very old version of ffmpeg, without possibility to update it to the latest version. Since a lot of changes/improvements are being made, inside ffmpeg's code, every day, it makes ffmpeg-php incompatible with the latest ffmpeg.
Read the official documentation for more info.
I covered the solution that helps most people that I spoke to about this in another thread.
https://stackoverflow.com/a/38626752/2074077
Essentially, your user needs access to the public directories.
<?php
exec('whoami'); // Gives you the running user; often it's www-data
?>
Then change your public ownership to the whoami user.
sudo chown -R www-data:root /var/www
This happened to me, I found this on another forum; the trick inside of PHP is to give the absolute path to FFMPEG. Try running it like this..
exec("/root/bin/ffmpeg -i input.avi -ab 56 -ar 44100 -b 200 -r 15 -s 320x240 -f flv output.flv",$output, $returnvalue);
Something about the fact that php is a different user than you are when typing straight into the command line. I guess it doesn't have the same shortcut list or whatever it is.
Not sure exactly why but it worked for me!
Good luck.

using ffmpeg exe in php

and am wanting to use ffmpeg as command line direct on windows 7 x64, I tried adding the extension, but unfortunately it did not work for some unknown reason, but the executable worked perfectly, I'm using the exec command, the only problem is that does not work with direct lines, I have to create a file. bat to run the program. this is my doubt.
$a = exec('\b.bat');
if ($a)
{
echo "Success"."\n";
print $a;
}else {
echo "No good"."\n";
print $a;
}
b.bat
ffmpeg -i video.flv -an -ss 00:00:16 -an -r 1 -vframes 1 -y %a.jpg
already tried several alternatives but the only one that worked was with the. bat
$a = exec('\windows/system32/ffmpeg.exe ffmpeg -i video.flv -an -ss 00:00:16 -an -r 1 -vframes 1 -y %a.jpg');
Make sure you include the full path to your .bat file and also make sure to include the full path to ffmpeg.exe in your .bat file.
Well, I am also working on this ffmpeg file to convert my movies and stuff my idea is for people to watch movies for free anyway here is what I use to convert my movies, have a note that when the ffmpeg.exe is not in the same folder than the movies then you will need to give a full path or use of ../ to go up a folder and / to a folder for example:
$input_path= $_FILES["file"]["tmp_name"];
$output_result= "movies/" . basename($_FILES["file"]["name"]).".flv";
exec("FFMPEG.EXE -i '$input_path' -s 900x400 -r 29.97 -b 1024k -ar 22050 -ab 50k -ac 1 '$output_result'");
there I am using scale and rateframe as well as bitrate and sound quality so my input and output I gave them in a variable basically that command you can use as is just give a value to those variables $input and $output. Please note that I am barely working out the idea too so far this is what I have come out with please do not expect excellent results since we are using microsoft windows the results may vary from version to version and you may have to use the command in administrator mode I hope this helps you my email is support#web2021.com if anything let me know man.

How to get PID from PHP function exec() in Windows?

I have always used:
$pid = exec("/usr/local/bin/php file.php $args > /dev/null & echo \$!");
But I am using an XP virtual machine to develop a web app and I have no idea how to get the pid in windows.
I tried this on a cmd:
C:\\wamp\\bin\\php\\php5.2.9-2\\php.exe "file.php args" > NUL & echo $!
And it gets the file executed, but the output is "$!"
How can I get the pid into the var $pid? (using php)
I'm using Pstools which allows you to create a process in the background and capture it's pid:
// use psexec to start in background, pipe stderr to stdout to capture pid
exec("psexec -d $command 2>&1", $output);
// capture pid on the 6th line
preg_match('/ID (\d+)/', $output[5], $matches);
$pid = $matches[1];
It's a little hacky, but it gets the job done
I landed here thanks to google and decided that this ten years old post needs more info based on How to invoke/start a Process in PHP and kill it using Process ID...
Imagine that you want to execute a command (this example uses ffmpeg to stream a file on a windows system to a rtmp server). You could command something like this:
ffmpeg -re -i D:\wicked_video.mkv -c:v libx264 -preset veryfast -b:v 200k -maxrate 400k -bufsize 6000k -pix_fmt yuv420p -g 50 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmp://<IP_OF_RMTP_SERVER>/superawesomestreamkey > d:\demo.txt 2> d:\demoerr.txt
The first part is explained here and the last part of that command outputs to files with that name for logging purposes:
> d:\demo.txt 2> d:\demoerr.txt
So lets assume that that command works. You tested it.
To run that command with php you can execute it with exec but it will take time (another subject, check set_time_limit), its a video handled by ffmpeg via php. Not the way to go but it is happening in this case.
You can run the command in background but what is the pid of that Process?
We want to kill it for some reason and psexec gives only the 'process ID" runned by a user. And there is only one user in this case.
We want multiple processes on the same user.
Here is a example to get the pid of a runned process in php:
// the command could be anything:
// $cmd = 'whoami';
// This is a f* one, The point is: exec is nasty.
// $cmd = 'shutdown -r -t 0'; //
// but this is the ffmpeg example that outputs seperate files for sake
$cmd = 'ffmpeg -re -i D:\wicked_video.mkv -c:v libx264 -preset veryfast -b:v 200k -maxrate 400k -bufsize 6000k -pix_fmt yuv420p -g 50 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmp://10.237.1.8/show/streamkey1 > d:\demo.txt 2> d:\demoerr.txt';
// we assume the os is windows, pipe read and write
$descriptorspec = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
];
// start task in background, when its a recource, you can get Parent process id
if ( $prog = is_resource( proc_open("start /b " . $cmd, $descriptorspec, $pipes ) ) )
{
// Get Parent process Id
$ppid = proc_get_status($prog);
// this is the 'child' pid
$pid = $ppid['pid'];
// use wmic to get the PID
$output = array_filter( explode(" ", shell_exec("wmic process get parentprocessid,processid | find \"$pid\"" ) ) );
array_pop($output);
// if pid exitst this will not be empty
$pid = end($output);
// outputs the PID of the process
echo $pid;
}
The code above should echo the pid of the 'inBackground' runned process.
Note that you need to save the pid to kill it later if it is still running.
Now you can do this to kill the process: (imagine the pid is 1234)
//'F' to Force kill a process
exec("taskkill /pid 1234 /F");
Here is my first post ever here on stackoverflow,
I hope this will help someone. Have a awesome and not lonely christmas ♪♪
You will have to install an extra extension, but found the solution located at Uniformserver's Wiki.
UPDATE
After some searching you might look into tasklist which coincidently, you may be able to use with the PHP exec command to get what you are after.
Here's a somewhat less "hacky" version of SeanDowney's answer.
PsExec returns the PID of the spawned process as its integer exit code. So all you need is this:
<?php
function spawn($script)
{
#exec('psexec -accepteula -d php.exe ' . $script . ' 2>&1', $output, $pid);
return $pid;
} // spawn
echo spawn('phpinfo.php');
?>
The -accepteula argument is needed only the first time you run PsExec, but if you're distributing your program, each user will be running it for the first time, and it doesn't hurt anything to leave it in for each subsequent execution.
PSTools is a quick and easy install (just unzip PSTools somewhere and add its folder to your path), so there's no good reason not to use this method.

Categories