I have an flv file uploaded to a server. I would like to display its duration in the following format "minutes:seconds". Can anybody help me with this ?
Thank you
There is also a FFMPEG PHP extension ie. apt-get install php5-ffmpeg then
$movie = new ffmepg_movie("path/to/movie.flv");
$duration_in_seconds = $movie->getDuration();
This has worked for me previously. The extension is good for retrieving meta-data and testing if an uploaded file is an FLV, etc.
Here is my code to grab a frame and generate the image from the video...
// get the duration and a random place within that
$cmd = "ffmpeg -i " . $videoPath . " 2>&1";
if (preg_match('/Duration: ((\d+):(\d+):(\d+))/s', `$cmd`, $time)) {
$total = ($time[2] * 3600) + ($time[3] * 60) + $time[4];
$second = rand(1, ($total - 1));
}
exec($cmd);
// get the screenshot
exec("ffmpeg -i " . $videoPath . " -deinterlace -an -ss $second -t 00:00:01 -r 1 -y -vcodec mjpeg -f mjpeg " . $imageOutput . " 2>&1");
$second variable is random number between 0 and total duration.
and the second exec() is to create a image file from selected frame.
$imageOutput is absolute path location to the generated image.
eg: /home/ariawan/image-generated.jpg
I´d use the getID3 PHP library, written in plain old PHP without any dependencies.
It not only gives you the duration of the .flv movie in seconds, but converts it to minute:seconds format already. Here is a code sample with v. 1.7.9 (latest stable version of getid3):
<?php
// getId3 library uses deprecated eregi_* functions
// which generate errors under PHP 5.3 - so I excluded them
error_reporting(E_ALL ^ E_DEPRECATED);
// just for debugging/sample
header('Content-Type: text/plain');
// include the getid3 base class in order to use the lib
require_once('./lib/getid3.php');
// path to your .flv file
$filename = './sample.flv';
$getID3 = new getID3();
$fileInfo = $getID3->analyze($filename);
// echoes something like 127.8743
print 'Playtime in seconds: ' . $fileInfo['playtime_seconds'];
print chr(10);
// echoes something like: 2:07
print 'Playtime in minute:seconds format: ' . $fileInfo['playtime_string'];
i am using php and ffmpeg to get duration of the video.
$cmd = "ffmpeg -i " . $videoPath . " 2>&1";
if (preg_match('/Duration: ((\d+):(\d+):(\d+))/s', `$cmd`, $time)) {
$total = ($time[2] * 3600) + ($time[3] * 60) + $time[4];
}
exec($cmd);
print_r() the $time variable to see.
make sure ffmpeg installed on your machine..
hope this will help.
Related
The PHP exec command is not executing the same as the shell's interaction.
cd /var/www/myfolder
zip -r /var/www/myfolder/temp/newfile.zip ./*
generates just a zip of files in the temp directory. However (simplified version):
$zip_dir = '/var/www/myfolder';
$temp_dir = $zip_dir . '/temp/';
chdir($zip_dir);
exec('zip -r ' . $temp_dir . 'newfile.zip ./*', $return);
generates the same zip but with the full path's of var and www (which results in two copies of myfolder so my file is twice as large as needed). The $return however has the same output as the command line execution. Both state only 15 files directories/folders were zipped. There is no mention of var or www in the PHP output.
I believe the chdir() command will not have any bearing on how the commands in exec() are run. So this might fix it:
$zip_dir = '/var/www/myfolder';
$temp_dir = $zip_dir . '/temp/';
$cmd = 'cd ' . escapeshellarg($zip_dir) . ' && zip -r ' . escapeshellarg($temp_dir . 'newfile.zip') . ' ./*';
exec($cmd, $return);
Note we always escape variables being passed to the command line.
But why not just zip within PHP?
<?php
$zip_target = "/var/www/myfolder";
$zip_file = "/var/www/myfolder/temp/newfile.zip";
$zip_temp = tempnam(sys_get_temp_dir(), 'a458');
$zip_obj = new \ZipArchive();
if ($zip_obj->open($zip_temp, ZIPARCHIVE::OVERWRITE)) {
$zip_obj->addGlob("$zip_target/**/*.*");
}
$zip_obj->close();
rename($zip_temp, $zip_file);
I am working on a php function used to upload a .wav to server (along with converting to mp3 and creating waveform image png) , and within the function I would like it to use soundtouch / soundstrech to detect the B.P.M. (Beats Per Minute). I know it will not be the most accurate but for my purposes it will be all I need.
I was able to get the B.P.M. of a .wav file using soundtouch / soundstrech along with ffmpeg within a test.php file using deven's php-bpm-detect wrapper But When I try to integrate it within my PHP function it returns the B.P.M. as zero.
I am wondering if there is a simpler way to get the bpm as a string from the following shell exec without having to use a separate php library?
I would like to perform this and have it return as a string:
$song_bpm = shell_exec('soundstretch ' . $file_path . ' -bpm');
test.php (This works and returns the proper bpm:)
<?php
require "class.bpm.php";
$wavfile = "38a2819c20.wav";
$bpm_detect = new bpm_detect($wavfile);
$test = $bpm_detect->detectBPM();
echo ' bpm of ' . $wavfile . ' is: ' . $test . ' ';
?>
PHP Function: (returns bpm as zero)
function upload_a_sound($user_id, $file_temp, $file_extn, $name, $uploader, $keywords) {
$timecode = substr(md5(time()), 0, 10);
$mp3name = 'beats/' . $timecode . '.mp3';
$file_path = 'beats/' . $timecode . '.wav';
move_uploaded_file($file_temp, $file_path);
shell_exec('ffmpeg -i ' . $file_path . ' -vn -ar 44100 -ac 2 -ab 192k -f mp3 ' . $mp3name . '');
require ('classAudioFile.php'); // This creates a spectogram .png file of .wav
$AF = new AudioFile;
$AF->loadFile($file_path);
$AF->visual_width=200;
$AF->visual_height=200;
$AF->visual_graph_color="#c491db";
$AF->visual_background_color="#000000";
$AF->visual_grid=false;
$AF->visual_border=false;
$AF->visual_graph_mode=0;
$AF->getVisualization ('images/song/' . $timecode . '.png');
$imageloc = 'images/song/' . $timecode . '.png';
require ('class.bpm.php'); //Deseven's class to get bpm,
$bpm_detect = new bpm_detect($file_path);
$song_bpm = $bpm_detect->detectBPM(); //when used here this returns 0
mysql_query("INSERT INTO `content` VALUES ('', '', '$name', '$uploader', '$keywords', '$file_path', '$imageloc', '$mp3name', '$song_bpm')"); // I will update this to mysqli soon, for now it works
}
I also found this which works, but not when I integrate it into my function:
// create new files, because we don't want to override the old files
$wavFile = $filename . ".wav";
$bpmFile = $filename . ".bpm";
//convert to wav file with ffmpeg
$exec = "ffmpeg -loglevel quiet -i \"" . $filename . "\" -ar 32000 -ac 1 \"" . $wavFile . "\"";
$output = shell_exec($exec);
// now execute soundstretch with the newly generated wav file, write the result into a file
$exec = "soundstretch \"" . $wavFile . "\" -bpm 2> " . $bpmFile;
shell_exec($exec);
// read and parse the file
$output = file_get_contents($bpmFile);
preg_match_all("!(?:^|(?<=\s))[0-9]*\.?[0-9](?=\s|$)!is", $output, $match);
// don't forget to delete the new generated files
unlink($wavFile);
unlink($bpmFile);
// here we have the bpm
echo $match[0][2];
I've updated my class so it's supporting absolute and relative paths now.
And the straightforward solution:
exec('soundstretch "test.wav" -bpm 2>&1',$average_bpm);
foreach ($average_bpm as $line) {
if (strpos($line,"Detected BPM rate") !== false) {
$line = explode(" ",$line);
$average_bpm = round($line[3]);
break;
}
}
echo $average_bpm;
Just keep in mind that $average_bpm will contain the error if anything goes wrong.
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();
How can I convert FLV to WMV? Is there any script around there or some way I can integrate this?
Thank you!!!
I don't think you can do this directly with PHP.
But, you can use external tools called form PHP (ffmpeg for example).
Here is a code sample:
<?php
$src = "file.flv";
$output = "file.wmv";
ffmpegPath = "/path/to/ffmpeg";
$flvtool2Path = "/path/to/flvtool2";
$ffmpegObj = new ffmpeg_movie($src);
$srcWidth = makeMultipleTwo($ffmpegObj->getFrameWidth());
$srcHeight = makeMultipleTwo($ffmpegObj->getFrameHeight());
$srcFPS = $ffmpegObj->getFrameRate();
$srcAB = intval($ffmpegObj->getAudioBitRate()/1000);
$srcAR = $ffmpegObj->getAudioSampleRate();
exec($ffmpegPath . " -i " . $src . " -ar " . $srcAR . " -ab " . $srcAB . " -vcodec wmv1 -acodec adpcm_ima_wav -s " . $srcWidth . "x" . $srcHeight . " " . $output. " | " . $flvtool2Path . " -U stdin " . $output);
// Make multiples function
function makeMultipleTwo ($value)
{
$sType = gettype($value/2);
if($sType == "integer")
{
return $value;
} else {
return ($value-1);
}
}
?>
Sources:
http://vexxhost.com/blog/2007/05/20/how-to-convertencode-files-to-flv-using-ffmpeg-php/
http://ubuntuforums.org/showpost.php?p=7315615&postcount=10
All solutions you will find are going to use ffmpeg, because that's easy to install on servers and even easier to utilize from PHP scripts. Most always you can just do:
exec("ffmpeg -i video.flv video.wmv");
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>";