I have a video encoding server set up on Laravel Forge with nginx. I'm trying to run a testing script to encode a video remotely via SSH, using the LaravelCollective SSH package.
This is my testing script (index.php)
<?php
exec("ffmpeg -I input.mpg -c:v libx264 -preset faster -crf 22 -c:a aac -strict experimental -movflags +faststart -vf scale=360:-1 output.mp4 1> output.txt 2>&1");
When I SSH into the server and run the script from the command line it works: the video is encoded; the script is working.
$ php /home/forge/mydomainname.com/public/test/index.php
However, when I run the same command locally in my Laravel app - using the SSH package - it doesn't encode and I receive no output; the browser just returns a white page.
SSH::run('php /home/forge/mydomainname.com/public/test/index.php', function($line) {
echo $line.PHP_EOL;
});
However however, if I open index.php, comment out the FFmpeg command and add some code to check if exec is enabled, it will execute and send output, so I know that the SSH package is actually working and executing the script remotely.
<?php
// exec("ffmpeg -I input.mpg -c:v libx264 -preset faster -crf 22 -c:a aac -strict experimental -movflags +faststart -vf scale=360:-1 output.mp4 1> output.txt 2>&1");
if(function_exists('exec')) {
echo "exec is enabled";
} else {
echo "exec is disabled";
}
It will return "exec is enabled" to my browser.
To sum up:
The script will encode video when running it via the command line on the server.
The script will not encode video when running it remotely.
The script will execute when running it remotely.
WTH?
I figured it out.
From within my Laravel app I decided to try echo exec('whoami'); and see if that returned anything to the browser. It did, so I knew exec() was working and I could trigger it via the SSH package.
Then I realized that my ffmpeg encoding command was suppressing output with 2>&1. I removed that and finally saw what was going on: I was receiving a "file not found" error, which was weird because input.mpg is in the same directory as index.php.
This has worked on three other servers, but not on this one created with Forge.
So I added the full path to the input file and voilĂ ! It works!
exec("ffmpeg -i /home/forge/mydomainname.com/public/test/input.mpg -c:v libx264 -preset faster -crf 22 -c:a aac -strict experimental -movflags +faststart -vf scale=360:-1 /home/forge/mydomainname.com/public/test/output.mp4 1> /home/forge/mydomainname.com/public/test/output.txt ");
Related
I'm using ffmpeg with PHP to compress the size of some videos I upload on my website, but I also want to capture the error If something goes wrong during the process when executing the command.
I have read that there's no way to capture the error, but I'm not sure if that's correct.
I hope someone will help me out with this doubt I have.
Here's my code:
$command="$ffmpeg -i $video['tmp_name'] -i pics/watermark.png -s $resolution -filter_complex [1:v][0:v]scale2ref=(127/60)*ih/10/sar:ih/10[wm][base];[base][wm]overlay=10:10 -pix_fmt yuv420p -c:a copy videos/$video_name";
shell_exec($command);
When I run exec() through php, I fail to get the output (stderr) into a file.
I've included "2> my_out_put_file.txt" at the end of my command to accomplish this. But when I include this, the command is not executed. However, if I run the command without "2> my_out_put_file.txt" then it works.
The interesting thing is that the whole command, even with "2> my_out_put_file.txt" at the end of the command works if I run directly via shell/promt, but not when I run it through php / apache.
Does not work:
exec("C:/FFmpeg/bin/ffmpeg -i $new_path/$filename_with_ext -f mp4 -vcodec libx264 -preset ultrafast -profile:v main -acodec aac $new_path/$filename.mp4 2> out.txt", $a, $b);
Works:
exec("C:/FFmpeg/bin/ffmpeg -i $new_path/$filename_with_ext -f mp4 -vcodec libx264 -preset ultrafast -profile:v main -acodec aac $new_path/$filename.mp4", $a, $b);
Best regardsNeo
This worked for me:
... 2>&1 >> log.txt
Tested from exec too:
php -r "exec('ffmpeg -version 2>&1 >> log.txt');"
I have read this Create Video using ffmpeg
Stack Question for the Same
Wiki Page for the same
Still I am not able to get it.
I have written this shell command in PHP
echo $make_movie = "$ffmpeg -framerate 1/5 -i $folder_name/img%03d.png -c:v libx264 -r 25 -pix_fmt yuv420p $folder_name/output.mp4";
This gives output
ffmpeg\bin\ffmpeg.exe -framerate 1/5 -i ankit/img%03d.png -c:v libx264 -r 25 -pix_fmt yuv420p ankit/output.mp4
if(shell_exec($make_movie)){
echo "<br />Movie Created..<br />";
}
else{
echo "<br />Movie Creation Error..<br />";
}
The Output is Movie Creation Error that means the Shell Command is not executing?
Questions:
What is wrong?
For future use, any debugging methods for this?
I ran the same command on cmd and it made the video..!!!
shell_exec is pretty useless when it comes to figuring out why your command failed. use exec() instead:
$last_line = exec($make_movie, $all_output, $exit_code);
var_dump($exit_code);
You'll have to look at ffmpeg's docs to determine what the exit code means
I have a php script which is creating a list of images (usually around 400) to feed to ffmpeg via an exec command, it breaks. Is there another way to send multiple images?
Sample code below
$imgs4vid = "'dir/img1.jpg' 'dir/img2.jpg' 'dir/img3.jpg' 'dir/img4.jpg' 'dir/img5.jpg' etc.."
exec("ffmpeg -r 1/5 -pattern_type glob -i ".$imgs4vid." -c:v libx264 -r 30 -pix_fmt yuv420p ".$vid_name.".mp4 2>&1", $output);
var_dump($output);
I have a way to do with -f concat -i (generatoe a list of files and put her)
Seems like a long way to do it though, must be an easier solution?
Thanks
You can use patterns for input (and output) file names:
ffmpeg -i /dir/img%d.jpg -s 640x480 -vcodec mjpeg /tmp/out.avi
I have an ip webcam which provides an MJPEG stream. I can successfully transcode and save that stream with ffmpeg under OSX. The following gives me pretty much what I want:
ffmpeg -f mjpeg -i "http://user:pass#10.0.1.200/nphMotionJpeg?Resolution=640x480&Quality=Standard" -b:v 1500k -vcodec libx264 /tmp/test.mp4
That will start an FFMPEG session and begin saving the live stream to my test.mp4 file. pressing q will quit ffmpeg and save the file.
I would like to programmatically start & stop the recording using a PHP or Bash shell script. I have tried the following:
<?php
$pid = pcntl_fork();
if($pid == -1){
die("could not fork");
}elseif($pid){
// we are the parent...
print $pid.' started recording. waiting 10 seconds...';
sleep(10); // Wait 10 seconds
print_r(shell_exec("kill ".$pid)); // Kill the child recording process
echo 'done';
exit();
}else{
// we are the child process. call ffmpeg.
exec('../lib/ffmpeg -f mjpeg -i "http://user:pass#10.0.1.200/nphMotionJpeg?Resolution=640x480&Quality=Standard" -b:v 1500k -vcodec libx264 /tmp/test.mp4');
}
But there are two problems:
The ffmpeg process does not end/die (probably because its forked again)
When I manually kill the ffmpeg process, the video file is not readable
So I was doing a combination of things wrong.
For starters, I needed to push the output form ffmpeg to a log file and also tell it to overwrite my temp file without prompting using the -y argument.
So instead of
ffmpeg -f mjpeg -i "http://user:pass#10.0.1.200/nphMotionJpeg?Resolution=640x480&Quality=Standard" -b:v 1500k -vcodec libx264 /tmp/test.mp4
I am now using
ffmpeg -y -f mjpeg -i "http://user:pass#10.0.1.200/nphMotionJpeg?Resolution=640x480&Quality=Standard" -b:v 1500k -vcodec libx264 /tmp/test.mp4 </dev/null >/dev/null 2>/tmp/ffmpeg.log &
The second problem was that I wasn't waiting long enough before sending the kill command to ffmpeg, and so a corrupt file was being created.
By adding the -t (for time limit) argument with 1 second, I determined that it takes an average of 15 seconds for ffmpeg to record 1 second of video. Increasing the time limit to 10 seconds made the average increase to 25 seconds, so it seems that on my server at least, theres 14 seconds of overhead. By increasing my sleep command in my php script to 30 seconds, I was able to get a useable video file.
So having PHP kill the ffmpeg process results in an unknown (or approximate at best) recording time length which is completely dependent on CPU power, network bandwidth, etc.
Thats a bit of a bummer because I had hoped to be able to increase the recording length depending on some external variables (I have insteon motion sensors feeding a database, i would like to record until the motion stops).
In any event, here is a working PHP script in case it helps someone in the future:
<?php
print date('H:i:s')."\nStarted recording. waiting 60 seconds...\n";
exec('../lib/ffmpeg -y -f mjpeg -i "http://user:pass#10.0.1.200/nphMotionJpeg?Resolution=640x480&Quality=Standard" -b:v 1500k -vcodec libx264 /tmp/test.mp4 </dev/null >/dev/null 2>/tmp/ffmpeg.log &');
sleep(60); // Wait long enough before killing, otherwise the video will be corrupt!
shell_exec('pkill ffmpeg'); // find and kill
echo "done\n\n";
exit();
?>
Send a SIGQUIT signal to the background process to terminate the ffmpeg command normally.
Just use
kill -s QUIT $PID
and the process will finish with a non-corrupted MP4 video file.
Send the "q" key:
process.StandardInput.WriteLine("q");