This is the main index.php file where I run my code to generate a video thumbnail with ffmpeg but it has no lucks at all I have been searching online for a lot of solution but nothing comes out i will be very appreciate if you guys can help me out. The shell_exec() keep giving me error
<html>
<head>
</head>
<body>
<form action="index.php" method="POST" enctype="multipart/form-data">
<input type="file" name="file"><input type="submit" name="submit" value="upload">
</form>
<?php
if(isset($_POST['submit'])){
$ffmpeg = "/Users/mac/Documents/ffmpeg/3.1.4/bin/ffmpeg";
$videoFile = $_FILES["file"]["tmp_name"];
$imageFile = "1.jpg";
$size = "320x240";
$getFromSecond = 5;
$cmd = "$ffmpeg -i $videoFile -an -ss $getFromSecond -s $size $imageFile 2>&1";
echo shell_exec($cmd);
echo shell_exec('whoami');
if(!shell_exec($cmd)){
echo "thumbnail created";
}else{
echo "error creating thumbnail";
}
}
?>
</body>
</html>
The user which is attempting to run this file does not have the correct permissions (usually www-data). To test this theory, try running with sudo:
shell_exec('sudo -u user -p pass -s $ffmpeg -i $videoFile -an -ss $getFromSecond -s $size $imageFile 2>&1');
You could also configure Apache to run your virtual host as a different user other than www-data or change permissions to the files you would like www-data to access:
https://unix.stackexchange.com/questions/30879/what-user-should-apache-and-php-be-running-as-what-permissions-should-var-www
Related
code works fine but if the filename has a single qoute just as "Britney's video.mp4" it does not work.
$ffmpeg = "/usb/bin/local/ffmpeg";
$videos = "/videos/*.mp4";
$ouput_path = "/videos/thumbnails/";
foreach(glob($videos) as $video_file){
$lfilename = basename($video_file);
$filename = basename($video_file, ".mp4");
$thumbnail = $ouput_path.$filename.'.jpg';
if (!file_exists($filename)) {
#$thumbnail = str_replace("'", "%27", $thumbnail);
exec("/usr/local/bin/ffmpeg -i '$video_file' -an -y -f mjpeg -ss 00:00:30 -vframes 1 '$thumbnail'");
}
echo "<a href='$lfilename'>$filename<img src='thumbnails/$filename.jpg' width='350'>";
i got it working but not using something overly complicated.
thanks all
$comd = "/usr/local/bin/ffmpeg -i \"$video_file\" -y -f mjpeg -ss 00:00:30 -vframes 1 \"$thumbnail\" 2>&1"; shell_exec($comd);
shell_exec($comd);
As suggested in the comments, you can just wrap all your shell-command String within the escapeshellarg() as shown below.
<?php
foreach(glob($videos) as $video_file){
$lfilename = basename($video_file);
$filename = basename($video_file, ".mp4");
$thumbnail = $ouput_path.$filename.'.jpg';
if (!file_exists($filename)) {
$cmd = "/usr/local/bin/ffmpeg -i ";
$cmd .= $video_file . " -an -y -f mjpeg -ss 00:00:30 ";
$cmd .= "-vframes 1 " . $thumbnail;
exec( escapeshellarg($cmd) );
}
I'm coding up a website back-end that will include user-uploaded video. In order to ensure maximum accessibility, I'm compressing the uploaded videos and re-saving them as .mp4 and .webm format to cover all browsers (or as many as possible anyway). To do this, I'm running an avconv command in the PHP exec() function.
I don't want to make the user wait for the script to finish before the page loads, so I'm running the code asynchronously. My code so far is below.
exec('bash -c "exec nohup setsid avconv -i ' . $tempPath . ' -c:v libx264 ' . $transpose . ' ' . $newPath . 'mp4 > /dev/null 2>/dev/null &"');
exec('bash -c "exec nohup setsid avconv -i ' . $tempPath . ' -c:v libvpx ' . $transpose . ' ' . $newPath . 'webm > /dev/null 2>/dev/null &"');
In addition to running the exec functions, I also save the video to a database and send the user an email thanking them for uploading their video.
Here's the rub: I want the server to WAIT until the video conversion is finished, and THEN add it to the database and send the user an email. Basically, the program flow would be:
User uploads video.
Video is placed in a temp folder.
User is taken to a thank you page indicating their video will be up shortly.
The server executes two avconv commands to convert and compress the video for web use.
Once BOTH conversions are finished, the video info is added to a MySQL database, an email is sent to the user, and the original uploaded video is deleted.
It may just be my ignorance of the command line (in fact it almost definitely is), but how could I 'queue up' these commands? First do both conversions, then call a PHP script to add to the database, then delete the original video, all while being asynchronous with the original PHP script?
EDIT: I've tried queuing them up with an '&&' operator, like below:
exec('bash -c "exec nohup setsid avconv -i ' . $tempPath . ' -c:v libx264 ' . $transpose . ' ' . $newPath . 'mp4 && avconv -i ' . $tempPath . ' -c:v libvpx ' . $transpose . ' ' . $newPath . 'webm > /dev/null 2>/dev/null &"');
However, that seems to cancel out the fact that I'm running it asynchronously, since the page now seems to wait for the command to finish.
You should start an asynchronous command line php script that encodes both videos and then sends an email :
upload.php :
exec('/usr/bin/php -f encode_files.php > /dev/null 2>/dev/null &"');
echo "Files will be encoded, come back later !";
encode_files.php
exec('avconv ...'); // Synchronously ! Without > /dev/null etc ...
exec('avconv ...'); // webm ...
mail('user#user.com', 'Encoding complete ! ', 'Great ! ');
I left the call as "bash -c exec ..." but i think there are shorter ways to call php scripts asynchronously :
Asynchronous shell exec in PHP
You can even pass params (like the user/video id, ...)
$cmd = 'nohup /usr/bin/php -f /path/to/php/file.php action=generate var1_id=23 var2_id=35 gen_id=535 > /path/to/log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);
You can disconnect the PHP script from the client but leave it running to complete your tasks.
// Your preliminary stuff here ...
/// then send the user elsewhere but carry on in the background
ignore_user_abort(true);
set_time_limit(0); // i.e. forever
header("Location: thankyoubutwait.php", true);
header("Connection: close", true);
header("Content-Encoding: none\r\n");
header("Content-Length: 0", true);
flush();
ob_flush();
session_write_close();
// more of your video stuff here including database writes
// and clean up bits
// (you may end up with zombie processes though so check your logs or write statuses to files etc.)
It's easy you just have to check the good execution of your command line like this:
// Your code before...
$command = 'bash -c "exec nohup setsid avconv -i ' . $tempPath . ' -c:v libx264 ' . $transpose . ' ' . $newPath . 'mp4 > /dev/null 2>/dev/null &"'
exec($command, $return, $status);
if($status == 0 ) {
$command2 = 'bash -c "exec nohup setsid avconv -i ' . $tempPath . ' -c:v libvpx ' . $transpose . ' ' . $newPath . 'webm > /dev/null 2>/dev/null &"';
exec($command2, $return2, $status2);
if($status2==0){
// let your user know your video traitement has been done
// lauch a new function for alert him
}
}
// Kill your process at end
die();
I have two files:
wget.php
<?php
include 'theme.php';
/*ceklogin();*/
css();
if($_POST['wget-send'])
{
$dir=$_POST['dir'];
$link=$_POST['link'];
exec('touch /tmp/wget-download-link.txt',$out);
exec('echo "'.$link.'" >> /tmp/wget-download-link.txt',$out);
exec('/www/wget_download.sh,$out);
echo $out[2];
exit();
}
echo "<form action=\"".$PHP_SELF."\" method=\"post\">";
echo "Download directory:<br><input type=\"text\" name=\"dir\" size=\"15\" value=\"/mnt/usb/\"/><br>";
echo '<br>Download link (one URL per line):<br>';
echo ("<textarea name=\"link\" rows=\"13\" cols=\"60\"></textarea><br><br>");
echo '<input type="submit" name="wget-send" value="Send" />';
echo "</form></div>";
foot();
echo '
</div>
</body>
</div>
</html>';
?>
wget_download.sh
while [ true ] ; do
urlfile=$( ls /tmp/wget-download-link.txt | head -n 1 )
if [ "$urlfile" = "" ] ; then
sleep 30
continue
fi
url=$( head -n 1 $urlfile )
if [ "$url" = "" ] ; then
mv $urlfile $urlfile.invalid
continue
fi
mv $urlfile $urlfile.busy
wget $url -o /tmp/wget.log -P $dir
mv $urlfile.busy $urlfile.done
done
How do I pass the variable from $dir in PHP to $dir in shell? so for example the $dir in my PHP is:
/mnt/usb
I want /mnt/usb to be executed in the wget_download.sh so it's gonna be like this:
wget $url -o /tmp/wget.log -P /mnt/usb
How do I do that?
First off, this is very very dangerous. Make sure you fully trust anyone able to access your site at all with running arbitrary commands on your server. I'd suggest ensuring SSL and proper password authentication.
With that caveat out of the way, you can us putenv("DIR=".$dir); See "http://php.net/manual/en/function.putenv.php" for a list of restrictions and configurations that might be required. You may need to prefix the variable names with "PHP_" in both php and in the shell script.
I second Daniels warning. That said I would just pass it as an argument:
dir="$1"
while [ true ] ; do
urlfile=$( ls /tmp/wget-download-link.txt | head -n 1 )
if [ "$urlfile" = "" ] ; then
sleep 30
continue
fi
url=$( head -n 1 $urlfile )
if [ "$url" = "" ] ; then
mv $urlfile $urlfile.invalid
continue
fi
mv $urlfile $urlfile.busy
wget $url -o /tmp/wget.log -P $dir
mv $urlfile.busy $urlfile.done
done
Which would change you exec call:
// the command would actually look like:
// /www/wget_download.sh /path/to/dir
exec('/www/wget_download.sh ' . escapeshellarg($dir),$out);
I am using the following code for uploading videos
$img1 = $_FILES['video']['name'];
if (!empty($img1)) {
$fname = $_FILES['video']['name'];
$img_name1 = "video/" . $fname;
if(move_uploaded_file($_FILES['video']['tmp_name'], $img_name1)){
$new_name = ShowFileName($fname);
$output = 'video/'.$new_name.'.flv';
$command = "$ffmpegpath -i $img_name1 -s 486x368 -b 400kb -ac 1 -ar 44100 -r 25 -s 320x240 -f flv $output";
$command = $ffmpegpath.' -i'.$img_name1.' -s 486x368 -b 400kb -ac 1 -ar 44100 -r 25 -qmin 3 -qmax 5 -y '.$output;
exec($command);
$thumb_dir = 'video_thumbs/';
$thumb = $new_name.'jpg';
exec($ffmpegpath .' -i '.$img_name1.' -an -y -f mjpeg -ss 0.05 -vframes 1 '.$thumb_dir.$img_name1);
unlink($img_name1);
}
}
It is working properly.ie Successfully moving video into videos folder and insering video name to database table. But the problem is related to the thumb image of this video. Thumb name was insert into database but the image wasn't uploding to the video_thumb folder.......
please help me....
The only problem I see is with the file name in the ffmpeg command line. If it contains special chars you should use escapeshellarg().
php function escapeshellarg
I need to read the output from ffmpeg in order to even try the solution to my question from yesterday. This is a separate issue from my problem there, so I made a new question.
How the heck do I get the output from an ffmpeg -i command in PHP?
This is what I've been trying:
<?PHP
error_reporting(E_ALL);
$src = "/var/videos/video1.wmv";
$command = "/usr/bin/ffmpeg -i " . $src;
echo "<B>",$command,"</B><br/>";
$command = escapeshellcmd($command);
echo "backtick:<br/><pre>";
`$command`;
echo "</pre><br/>system:<br/><pre>";
echo system($command);
echo "</pre><br/>shell_exec:<br/><pre>";
echo shell_exec($command);
echo "</pre><br/>passthru:<br/><pre>";
passthru($command);
echo "</pre><br/>exec:<br/><pre>";
$output = array();
exec($command,$output,$status);
foreach($output AS $o)
{
echo $o , "<br/>";
}
echo "</pre><br/>popen:<br/><pre>";
$handle = popen($command,'r');
echo fread($handle,1048576);
pclose($handle);
echo "</pre><br/>";
?>
This is my output:
<B>/usr/bin/ffmpeg -i /var/videos/video1.wmv</B><br/>
backtick:<br/>
<pre></pre><br/>
system:<br/>
<pre></pre><br/>
shell_exec:<br/>
<pre></pre><br/>
passthru:<br/>
<pre></pre><br/>
exec:<br/>
<pre></pre><br/>
popen:<br/>
<pre></pre><br/>
I don't get it. safe_mode is off. There's nothing in disable_functions. The directory is owned by www-data (the apache user on my Ubuntu system). I get a valid status back from exec() and system() and running the same command from the command line give me tons of output. I feel like I must be missing something obvious but I have no idea what it is.
The problem is you catch only stdout and not stderr (see Standard Streams).
Change this line:
$command = "/usr/bin/ffmpeg -i " . $src;
into
$command = "/usr/bin/ffmpeg -i " . $src . " 2>&1";
and give it another try :)
Use ffprobe instead, it's much quicker and supports JSON output.
$output = shell_exec('ffprobe -v quiet -print_format json -show_format -show_streams "path/to/yourfile.ext"');
$parsed = json_decode($output, true);
And you have all your video info in a php array! This is much faster than ffmpeg -i for some reason.
To get output status and output:
exec("ffmpeg -i input.avi output.mp4 2>&1", $output, $returnStatus);
print_r($output);
if($returnStatus === 0){
// success
}
else {
//fail
}
You can use exec and print_r the output...
exec("ffmpeg -i input.avi -vcodec h264 -acodec aac -strict -2 output.mp4 2>&1",$output);
echo "<pre>";
print_r($output);
echo "</pre>";