How to get duration a video - laravel - laravel

I want to get the duration of my videos when I upload them.
To do this, I upload my videos like this:
$video = Carbon::now()->timestamp . '_' .
$request->file('video')->getClientOriginalName();
$request->file('video')->move(
$this->getCorrectPathOnServerAndLocal('/assets/videos/videos'), $video
);
My movie is uploaded well.
now I want to get the duration of this video.
I'm using PHP-FFMpeg:
composer require php-ffmpeg/php-ffmpeg
$ffprobe = FFProbe::create(); //error
dd("test");
$duration = $ffprobe
->format($this->getCorrectPathOnServerAndLocal('/assets/videos/videos').$video) // extracts file informations
->get('duration');
but I got this error:
(2/2) ExecutableNotFoundException
Unable to load FFProbe
in FFProbeDriver.php (line 50)
at FFProbeDriver::create(array(), null)in FFProbe.php (line 207)

first install getID3 by composer require james-heinrich/getid3
then
$getID3 = new \getID3;
$file = $getID3->analyze($video_path);
$duration = date('H:i:s.v', $file['playtime_seconds']);

I personally created a Video CMS and found the easiest way to be using ID3 as follows:
public function getDuration($full_video_path)
{
$getID3 = new \getID3;
$file = $getID3->analyze($full_video_path);
$playtime_seconds = $file['playtime_seconds'];
$duration = date('H:i:s.v', $playtime_seconds);
return $duration;
}
Before I used ffmpeg like this:
// Get the video duration
$parameters = "2>&1 | grep Duration | cut -d ' ' -f 4 | sed s/,//";
$cmd_duration_ffmpeg = "$ffmpeg_path -i $full_video_path $parameters";
$duration = shell_exec($cmd_duration_ffmpeg);
Both options will work perfectly, choose whichever works best for you.

Using FFMPEG PHP library, and using FFprobe like this to get video duration in seconds:
$ffprobe = FFProbe::create();
$duration = $ffprobe->format($request->file('video'))->get('duration');
$duration = explode(".", $duration)[0];

Related

How to read length of video file from cmd

I have a bunch of mp4 files in a folder and I want to create a text file with all the names and the length of the files as in:
01_Welcome.mp4 00.01.23
02_Tools.mp4 00.03.12
I know how to read the names of the files buy how do I get the length attribute? When I click a file the length appears in the status bar, so there should be a way to read that property. And I would like to do it from the command line, not through a third-party package.
In ubuntu there you can
ffmpeg -i myvideo 2>&1 | grep Duration | cut -d ' ' -f 4 | sed s/,//
But in Windows MediaInfo is the one option
In Windows' PowerShell you can do the following to extract length of a single media file:
$Folder = 'C:\Path\To\Parent\Folder'
$File = 'Video.mp4'
$LengthColumn = 27
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace($Folder)
$objFile = $objFolder.ParseName($File)
$Length = $objFolder.GetDetailsOf($objFile, $LengthColumn)
Iteration over the folder content is left as an exercise for the reader.
Source

ffmpeg 1 image with many frames

Is that possible to create thumbnails of video using ffmpeg in this format:
I need to output a single image with vertical shots every 10 seconds.
I know only how to create one image with one frame:
<?php
$ffmpeg = '/usr/local/bin/ffmpeg';
$video = '1.mp4';
$image = '1.png';
$interval = 1;
$size = '300x210';
$cmd = "$ffmpeg -i $video -deinterlace -an -ss $interval -f mjpeg -t 1 -r 1 -y -s $size $image 2>&1";
$return = `$cmd`;
?>
You can do this with one ffmpeg command.
Example
ffmpeg -i alone_in_the_wilderness.mp4 -filter_complex \
"select='isnan(prev_selected_t)+gte(t-prev_selected_t\,10)',yadif,scale=240:-1,tile=1x3" \
-vframes 1 -t 30 -q:v 4 strip.jpg
Example with borders
tile=1x3:margin=10:padding=10
Also see
select, yadif, scale, tile filters documentation
Combine multiple images to form a strip of images ffmpeg
FFmpeg output screenshot gallery
You can get a single image with one frame every 10 seconds with ffmpeg (e.g. 1.png, 2.png, 3.png) in a for loop and then merge the images horizontally using imagemagick:
convert 1.png 2.png 3.png -append vertical.png

ffmpeg php convert in background

Hier is the deal: I converted my file with next command:
$output = exec("ffmpeg -i ".$directory_path_full." -ar 22050 -ab 32 -f flv -s 320x240 ".$directory_path.$file_name.".flv");
But now I need to tell database that processing is over! how to do that?
if converting is done, insert into database table videos row converted to 1.
also found this script:
$output = shell_exec('ffmpeg ' . escapeshellarg($directory_path_full) . ' ' . escapeshellarg($directory_path.$file_name.".flv"));
and again how to update database that completed?
exec() will not return until whatever you're executing completes, so basically:
exec("ffmpeg blah blah blah", $output, $return_var);
if ($return_val = "whatever value indicates success") {
... update database to indicate success ...
}
Don't assume the conversion succeeded and blindly update the database, always check if things actually succeeded.

When converting .mov to .flv video plays horizontally

When I record a video (.mov) through my iPhone it display vertically which is right.
But after converting the .mov to .flv(using ffmpeg) it displays horizontally.
My code:
function convert_flv($vidtime,$infile, $outfile, $w = 0, $h = 0, $extra_infile = '', $extra_outfile = '') {
$parms = '';
if($w == 0 && $h == 0) {
//$parms .= '-sameq ';
} else {
$parms = '-s {$w}x{$h} ';
}
if($vidtime==60) {
$cmd = ffmpeg($infile, $outfile, $parms.' '.$extra_infile, '-t 00:01:00 -ar 22050 -r 15 -f flv '.$extra_outfile);
} else {
$cmd = ffmpeg($infile, $outfile, $parms.' '.$extra_infile, '-t 00:04:00 -ar 22050 -r 15 -f flv '.$extra_outfile);
}
print_r($cmd);
return $cmd;
}
iPhone's store orientation information in .mov metadata that ffmpeg ignores, leading to rotated output. Correctly parsing the metadata is a problem.
If you're recording movies in a consistent orientation you can rotate them by adding -vf "transpose=1" to your ffmpeg command. Docs for transpose.
The orientation is a meta-data field in the video file - the actual file is not recorded in an alternate orientation. You would need to apply a transform in ffmpeg to rotate the video.

Can ffmpeg show a progress bar?

I am converting a .avi file to .flv file using ffmpeg. As it takes a long time to convert a file I would like to display a progress bar. Can someone please guide me on how to go about the same.
I know that ffmpeg somehow has to output the progress in a text file and I have to read it using ajax calls. But how do I get ffmpeg to output the progress to the text file?
I've been playing around with this for a few days. That "ffmpegprogress" thing helped, but it was very hard to get to work with my set up, and hard to read the code.
In order to show the progress of ffmpeg you need to do the following:
run the ffmpeg command from php without it waiting for a response (for me, this was the hardest part)
tell ffmpeg to send it's output to a file
from the front end (AJAX, Flash, whatever) hit either that file directly or a php file that can pull out the progress from ffmpeg's output.
Here's how I solved each part:
1.
I got the following idea from "ffmpegprogress". This is what he did: one PHP file calls another through an http socket. The 2nd one actually runs the "exec" and the first file just hangs up on it. For me his implementation was too complex. He was using "fsockopen". I like CURL. So here's what I did:
$url = "http://".$_SERVER["HTTP_HOST"]."/path/to/exec/exec.php";
curl_setopt($curlH, CURLOPT_URL, $url);
$postData = "&cmd=".urlencode($cmd);
$postData .= "&outFile=".urlencode("path/to/output.txt");
curl_setopt($curlH, CURLOPT_POST, TRUE);
curl_setopt($curlH, CURLOPT_POSTFIELDS, $postData);
curl_setopt($curlH, CURLOPT_RETURNTRANSFER, TRUE);
// # this is the key!
curl_setopt($curlH, CURLOPT_TIMEOUT, 1);
$result = curl_exec($curlH);
Setting CURLOPT_TIMEOUT to 1 means it will wait 1 second for a response. Preferably that would be lower. There is also the CURLOPT_TIMEOUT_MS which takes milliseconds, but it didn't work for me.
After 1 second, CURL hangs up, but the exec command still runs. Part 1 solved.
BTW - A few people were suggesting using the "nohup" command for this. But that didn't seem to work for me.
*ALSO! Having a php file on your server that can execute code directly on the command line is an obvious security risk. You should have a password, or encode the post data in some way.
2.
The "exec.php" script above must also tell ffmpeg to output to a file. Here's code for that:
exec("ffmpeg -i path/to/input.mov path/to/output.flv 1> path/to/output.txt 2>&1");
Note the "1> path/to/output.txt 2>&1". I'm no command line expert, but from what I can tell this line says "send normal output to this file, AND send errors to the same place". Check out this url for more info: http://tldp.org/LDP/abs/html/io-redirection.html
3.
From the front end call a php script giving it the location of the output.txt file. That php file will then pull out the progress from the text file. Here's how I did that:
// # get duration of source
preg_match("/Duration: (.*?), start:/", $content, $matches);
$rawDuration = $matches[1];
// # rawDuration is in 00:00:00.00 format. This converts it to seconds.
$ar = array_reverse(explode(":", $rawDuration));
$duration = floatval($ar[0]);
if (!empty($ar[1])) $duration += intval($ar[1]) * 60;
if (!empty($ar[2])) $duration += intval($ar[2]) * 60 * 60;
// # get the current time
preg_match_all("/time=(.*?) bitrate/", $content, $matches);
$last = array_pop($matches);
// # this is needed if there is more than one match
if (is_array($last)) {
$last = array_pop($last);
}
$curTime = floatval($last);
// # finally, progress is easy
$progress = $curTime/$duration;
Hope this helps someone.
There is an article in Russian which describes how to solve your problem.
The point is to catch Duration value before encoding and to catch time=... values during encoding.
--skipped--
Duration: 00:00:24.9, start: 0.000000, bitrate: 331 kb/s
--skipped--
frame= 41 q=7.0 size= 116kB time=1.6 bitrate= 579.7kbits/s
frame= 78 q=12.0 size= 189kB time=3.1 bitrate= 497.2kbits/s
frame= 115 q=13.0 size= 254kB time=4.6 bitrate= 452.3kbits/s
--skipped--
It’s very simple if you use the pipeview command. To do this, transform
ffmpeg -i input.avi {arguments}
to
pv input.avi | ffmpeg -i pipe:0 -v warning {arguments}
No need to get into coding!
You can do it with ffmpeg's -progress argument and nc
WATCHER_PORT=9998
DURATION= $(ffprobe -select_streams v:0 -show_entries "stream=duration" \
-of compact $INPUT_FILE | sed 's!.*=\(.*\)!\1!g')
nc -l $WATCHER_PORT | while read; do
sed -n 's/out_time=\(.*\)/\1 of $DURATION/p')
done &
ffmpeg -y -i $INPUT_FILE -progress localhost:$WATCHER_PORT $OUTPUT_ARGS
FFmpeg uses stdout for outputing media data and stderr for logging/progress information. You just have to redirect stderr to a file or to stdin of a process able to handle it.
With a unix shell this is something like:
ffmpeg {ffmpeg arguments} 2> logFile
or
ffmpeg {ffmpeg arguments} 2| processFFmpegLog
Anyway, you have to run ffmpeg as a separate thread or process.
Sadly, ffmpeg itself still cannot show a progress bar – also, many of the aforementioned bash- or python-based stop-gap solutions have become dated and nonfunctional.
Thus, i recommend giving the brand-new ffmpeg-progressbar-cli a try:
It's a wrapper for the ffmpeg executable, showing a colored, centered progress bar and the remaining time.
Also, it's open-source, based on Node.js and actively developed, handling most of the mentioned quirks (full disclosure: i'm its current lead developer).
If you just need hide all info and show default progress like ffmpeg in last line, you can use -stats option:
ffmpeg -v warning -hide_banner -stats ${your_params}
I found ffpb Python package (pip install ffpb) that passes arguments transparently to FFmpeg. Due to it's robustness, it doesn't need much maintenance. The last release is from Apr 29, 2019.
https://github.com/althonos/ffpb
javascript should tell php to start converting [1] and then do [2] ...
[1] php: start conversion and write status to file (see above):
exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");
For the second part we need just javascript to read the file.
The following example uses dojo.request for AJAX, but you could use jQuery or vanilla or whatever as well :
[2] js: grab the progress from the file:
var _progress = function(i){
i++;
// THIS MUST BE THE PATH OF THE .txt FILE SPECIFIED IN [1] :
var logfile = 'path/to/output.txt';
/* (example requires dojo) */
request.post(logfile).then( function(content){
// AJAX success
var duration = 0, time = 0, progress = 0;
var resArr = [];
// get duration of source
var matches = (content) ? content.match(/Duration: (.*?), start:/) : [];
if( matches.length>0 ){
var rawDuration = matches[1];
// convert rawDuration from 00:00:00.00 to seconds.
var ar = rawDuration.split(":").reverse();
duration = parseFloat(ar[0]);
if (ar[1]) duration += parseInt(ar[1]) * 60;
if (ar[2]) duration += parseInt(ar[2]) * 60 * 60;
// get the time
matches = content.match(/time=(.*?) bitrate/g);
console.log( matches );
if( matches.length>0 ){
var rawTime = matches.pop();
// needed if there is more than one match
if (lang.isArray(rawTime)){
rawTime = rawTime.pop().replace('time=','').replace(' bitrate','');
} else {
rawTime = rawTime.replace('time=','').replace(' bitrate','');
}
// convert rawTime from 00:00:00.00 to seconds.
ar = rawTime.split(":").reverse();
time = parseFloat(ar[0]);
if (ar[1]) time += parseInt(ar[1]) * 60;
if (ar[2]) time += parseInt(ar[2]) * 60 * 60;
//calculate the progress
progress = Math.round((time/duration) * 100);
}
resArr['status'] = 200;
resArr['duration'] = duration;
resArr['current'] = time;
resArr['progress'] = progress;
console.log(resArr);
/* UPDATE YOUR PROGRESSBAR HERE with above values ... */
if(progress==0 && i>20){
// TODO err - giving up after 8 sec. no progress - handle progress errors here
console.log('{"status":-400, "error":"there is no progress while we tried to encode the video" }');
return;
} else if(progress<100){
setTimeout(function(){ _progress(i); }, 400);
}
} else if( content.indexOf('Permission denied') > -1) {
// TODO - err - ffmpeg is not executable ...
console.log('{"status":-400, "error":"ffmpeg : Permission denied, either for ffmpeg or upload location ..." }');
}
},
function(err){
// AJAX error
if(i<20){
// retry
setTimeout(function(){ _progress(0); }, 400);
} else {
console.log('{"status":-400, "error":"there is no progress while we tried to encode the video" }');
console.log( err );
}
return;
});
}
setTimeout(function(){ _progress(0); }, 800);
These answers that use multiple tools/consoles are complicating things too much.
pv is a good option but has the noted drawbacks of missing non-senquential data.
Just use the progress utility: Run ffmpeg as normal then in another console monitor with progress -m -c ffmpeg
Calling php's system function blocks that thread, so you'll need to spawn off 1 HTTP request for performing the conversion, and another polling one for reading the txt file, that's being generated.
Or, better yet, clients submit the video for conversion and then another process is made responsible for performing the conversion. That way the client's connection won't timeout while waiting for the system call to terminate. Polling is done in the same way as above.
Had problems with the second php part. So I am using this instead:
$log = #file_get_contents($txt);
preg_match("/Duration:([^,]+)/", $log, $matches);
list($hours,$minutes,$seconds,$mili) = split(":",$matches[1]);
$seconds = (($hours * 3600) + ($minutes * 60) + $seconds);
$seconds = round($seconds);
$page = join("",file("$txt"));
$kw = explode("time=", $page);
$last = array_pop($kw);
$values = explode(' ', $last);
$curTime = round($values[0]);
$percent_extracted = round((($curTime * 100)/($seconds)));
Outputs perfectly.
Would like to see something for multiple uploads for another progress bar. This passing for the current file for one percentage. Then an overall progress bar. Almost there.
Also, if people are having a hard time getting:
exec("ffmpeg -i path/to/input.mov path/to/output.flv 1> path/to/output.txt 2>&1");
To work.
Try:
exec("ffmpeg -i path/to/input.mov path/to/output.flv 1>path/to/output.txt 2>&1");
"1> path" to "1>path" OR "2> path" to "2>path"
Took me awhile to figure it out. FFMPEG kept failing. Worked when I changed to no space.
By taking the progress bar from this link, I create a simple script that shows the actual frame of the video being encoded and shows a progress bar at the bottom of it... at the same time that ffmpeg encodes the video, of course.
First, we have to get duration, width and height from the video, to create the bar. But, as color filter can't get this information from the file, we have to get them first with ffprobe. Then, we use them with ffmpeg.
#!/bin/bash
video_duration=`ffprobe -v quiet -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1"`
video_width=`ffprobe -v quiet -select_streams v -show_entries stream=width -of csv=p=0:s=x "$1"`
video_height=`ffprobe -v quiet -select_streams v -show_entries stream=height -of csv=p=0:s=x "$1"`
five_percent=`expr $video_height / 20`
#echo $video_duration
#echo $video_width
#echo $video_height
#echo $five_percent
ffmpeg -i "$1" -filter_complex "color=c=red:s='$video_width'x$five_percent[bar];[0][bar]overlay=-w+(w/$video_duration)*t:H-h:shortest=1[bar]" "$2" -map [bar] -f xv display
Then, use script as:
sh encode_with_bar.sh video_in.mkv video_out.mp4
Performance: the filter used is very simple... but everything added consumes additional CPU. Testing a 10MB video file in my computer, this is the difference:
Without script: 14.46 seconds
With script: 17.05 seconds (18% more)
Yes, almost 20% more. For short videos, it's nice. For larger files, probably it's not a good idea 🤷.
This is my solution:
I use ffpb and python subprocess to tracking ffmpeg progress. Then I push status to a database (ex: Redis) for display a progress bar on website.
import subprocess
cmd = 'ffpb -i 400MB.mp4 400MB.avi'
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
encoding='utf-8',
errors='replace'
)
while True:
realtime_output = process.stdout.readline()
if realtime_output == '' and process.poll() is not None:
break
if realtime_output:
print(realtime_output.strip(), flush=True)
print('Push status to Redis...')
After some investigation of this library I see that implementation should follow these steps:
Calc result duration. E.g. get initial duration (substract any trim seconds if required):
ffprobe -v error -show_entries format=duration -of csv=p=0 input.mkv
Result must be parsed as float and stored, e.g. in JavaScript:
const duration = parseFloat(result);
In task - specify additional flag, so ffmpeg will output processed duration:
ffmpeg -i input.mkv -progress pipe:1 ...
Or the same but pipe to stderr:
ffmpeg -i input.mkv -progress pipe:2 ...
Extract processed duration (from stdout or stderr), e.g. in JavaScript:
const matchedResult = chunkResult.match(/out_time_ms=(\d+)/);
if (Array.isArray(matchedResult)) {
const currentTimeMs = Math.round(Number(matchedResult[1]) / 1000000);
}
Compare processed duration with total duration
currentTimeMs/duration
Keeping it simple &… Here's a brief summary of the possibilities for using a progress bar.
No problem with pv (istefani's suggestion) for converting, e.g. YouTube videos. Not working for videos downloaded from Odysee though. I've got this error message:
[mov,mp4,m4a,3gp,3g2,mj2 # 0x7fee39005400] stream 0, offset 0x30: partial file [mov,mp4,m4a,3gp,3g2,mj2 # 0x7fee39005400] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(tv, bt709), 1280x720, 604 kb/s): unspecified pixel format Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
I ended up using ffpb, which still works great for me; no issues so far.
Using progress -m -c ffmpeg … is interesting, but one needs to open another console to run it after executing the normal ffmpeg command in the first console (not convenient if running from a shell script).
ffmpeg-progress-yield seems to be an excellent alternative to ffpb, but does not show (at least, for me) the name of the video being converted; it shows “test” instead of the actual filename.
Finally, ffmpeg-progressbar-cli is very similar to ffpb and ffmpeg-progress-yield, but apparently, is no longer maintained; I haven't tried it.

Resources