I'm trying to generate thumbnails from random points in a movie using FFMPEG and FFMPEG-PHP extension.
My script works OK.. however takes 20 minutes just to generate 5-10 thumbnails!!
The script works by generating random numbers which are used as frame numbers later. All numbers generated are within the movies frame count.
Can you work out why this script is taking 20 mins to finish? If not, a better solution?
<?php
//Dont' timeout
set_time_limit(0);
//Load the file (This can be any file - still takes ages)
$mov = new ffmpeg_movie('1486460.mp4');
//Get the total frames within the movie
$total_frames = $mov->getFrameCount();
//Loop 5-10 times to generate random frames 5-10 times
for ($i = 1; $i <= 5; ) {
// Generate a number within 200 and the total number of frames.
$frame = mt_rand(200,$total_frames);
$getframe = $mov->getFrame($frame);
// Check if the frame exists within the movie
// If it does, place the frame number inside an array and break the current loop
if(开发者_运维问答$getframe){
$frames[$frame] = $getframe ;
$i++;
}
}
//For each frame found generate a thumbnail
foreach ($frames as $key => $getframe) {
$gd_image = $getframe->toGDImage();
imagejpeg($gd_image, "images/shot_".$key.'.jpeg');
imagedestroy($gd_image);
echo $key.'<br/>';
}
?>
The script SHOULD be generating frame numbers which are valid? Anything within START - END should be valid frame numbers? Yet the loop takes ages!
You could invoke ffmpeg from the commandline, using the -ss
switch to seek to an approrpriate start-point (in time, not in number of frames) and -vframes 1
to tell it to extract exactly one frame, e.g.:
ffmpeg -i 1486460.mp4 -ss 10 -vframes 1 images/shot_10.jpg
Will extract a frame from 10 seconds in and call it images/shot_10.jpg
The problem here is the word random. I succesful got the duration of the video and then try to get one frame with that random duration. Easily modifiable for more frames:
$cmd = "ffmpeg -i {$src} 2>&1 |grep Duration";
$output = array ();
exec($cmd, $output);
if(count($output)) {
$duration = explode(':', trim(str_replace('Duration:', NULL, current(explode(',', current($output))))));
list($hour, $min, $sec) = $duration;
$sec = sprintf("%02d:%02d:%02d", rand(0, $hour), rand(0, $min), rand(0, $sec));
} else {
$sec = "00:00:12"; //12sec it's ok :)
}
$cmd = "ffmpeg -ss {$sec} -i {$src} -s {$w}x{$h} -f image2 -vframes 1 {$destination}";
$output = array ();
exec($cmd, $output);
I don't know a great deal about film formats, but since the compression probably relies on a single pass delta compression with reference frames, if the position of the reference frames is not defined, this means that the system has to play the full movie to get to a particular offset. It'd be easy to check this by always loading, say, looking for a frame between offset 20 and the number of frames divided by some value.
Assuming this is the case, you have a further issue in that the way you've designed your algorithm, you need to rewind to the start and search forward for each of the 5 frames - if you generated your offsets in advance, then sorted the list, ffmpeg may be able to fetch the frames in a single pass.
HTH
C.
You have written a mistake in your loop.
if($getframe) {
$frames[$frame] = $getframe;
$i++;
}
Lets say the for loop runs infinitely until it is broken. If the if statement is false the first time, so $i++
cannot be reached and the loop then is endless.
You need to place the $i++
at the bottom of the for loop, then the execution will be 2 seconds instead of 20 minutes.
精彩评论