Ok. I have a fully-functional solution. This is for anyone who finds this question wanting to do the same thing. My code may not be very elegant, but it gets the job done.
Getting FFMPEG's output
First I had to get the output from ffmpeg -i, which was a challenge in itself. Thanks to hegemon's answer on my other question, I was finally able to get it working with 2>&1
at the end of my command. And thanks to Evert's answer to this question, I was able to parse the output with preg_match
to find the original file's height and width.
function get_vid_dim($file)
{
$command = '/usr/bin/ffmpeg -i ' . escapeshellarg($file) . ' 2>&1';
$dimensions = array();
exec($command,$output,$status);
if (!preg_match('/Stream #(?:[0-9\.]+)(?:.*)\: Video: (?P<videocodec>.*) (?P<width>[0-9]*)x(?P<height>[0-9]*)/',implode('\n',$output),$matches))
{
preg_match('/Could not find codec parameters \(Video: (?P<videocodec>.*) (?P<width>[0-9]*)x(?P<height>[0-9]*)\)/',implode('\n',$output),$matches);
}
if(!empty($matches['width']) && !empty($matches['height']))
{
$dimensions['width'] = $matches['width'];
$dimensions['height'] = $matches['height'];
}
return $dimensions;
}
Determining the best dimensions
I wrote this function to determine the best dimensions to use for conversion. It takes the original's dimensions, target dimensions, and whether or not to force conversion to the target aspect ratio (determined from its width/height). The target dimensions will always be the largest result this function can return.
function get_dimensions($original_width,$original_height,$target_width,$target_height,$force_aspect = true)
{
// Array to be returned by this function
$target = array();
// Target aspect ratio (width / height)
$aspect = $target_width / $target_height;
// Target reciprocal aspect ratio (height / width)
$raspect = $target_height / $target_width;
if($original_width/$original_height !== $aspect)
{
// Aspect ratio is different
if($original_width/$original_height > $aspect)
{
// Width is the greater of the two dimensions relative to the target dimensions
if($original_width < $target_width)
{
// Original video is smaller. Scale down dimensions for conversion
$target_width = $original_width;
$target_height = round($raspect * $target_width);
}
// Calculate height from width
$original_height = round($original_height / $original_width * $target_width);
$original_width = $target_width;
if($force_aspect)
{
// Pad top and bottom
$dif = round(($target_height - $original_height) / 2);
$target['padtop'] = $dif;
$target['padbottom'] = $dif;
}
}
else
{
// Height is the greater of the two dimensions relative to the target dimensions
if($original_height < $target_height)
{
// Original video is smaller. Scale down dimensions for conversion
$target_height = $original_height;
$target_width = round($aspect * $target_height);
}
//Calculate width from height
$original_width = round($original_width / $original_height * $target_height);
$original_height = $target_height;
if($force_aspect)
{
// Pad left and right
$dif = round(($target_width - $original_width) / 2);
$target['padleft'] = $dif;
$target['padright'] = $dif;
}
}
}
else
{
// The aspect ratio is the same
if($original_width !== $target_width)
{
if($original_width < $target_width)
{
// The original video is smaller. Use its resolution for conversion
$target_width = $original_width;
$target_height = $original_height;
}
else
{
// The original video is larger, Use the target dimensions for conversion
$original_width = $target_width;
$original_height = $target_height;
}
}
}
if($force_aspect)
{
// Use the target_ vars because they contain dimensions relative to the target aspect ratio
$target['width'] = $target_width;
$target['height'] = $target_height;
}
else
{
// Use the original_ vars because they contain dimensions relative to the original's aspect ratio
$target['width'] = $original_width;
$target['height'] = $original_height;
}
return $target;
}
Usage
Here are a few examples of what you will get from get_dimensions()
to make things more clear:
get_dimensions(480,360,640,480,true);
-returns: Array([width] => 480, [height] => 360)
get_dimensions(480,182,640,480,true);
-returns: Array([padtop] => 89, [padbottom] => 89, [width] => 480, [height] => 360)
get_dimensions(480,182,640,480,false);
-returns: Array([width] => 480, [height] => 182)
get_dimensions(640,480,480,182,true);
-returns: Array([padleft] => 119, [padright] => 119, [width] => 480, [height] => 182)
get_dimensions(720,480,640,480,true);
-returns: Array([padtop] => 27, [padbottom] => 27, [width] => 640, [height] => 480)
get_dimensions(720,480,640,480,false);
-returns: Array([width] => 640, [height] => 427)
The Finished Product
Now, to put it all together:
$src = '/var/videos/originals/original.mpg';
$original = get_vid_dim($src);
if(!empty($original['width']) && !empty($original['height']))
{
$target = get_dimensions($original['width'],$original['height'],640,480,true);
$command = '/usr/bin/ffmpeg -i ' . $src . ' -ab 96k -b 700k -ar 44100 -s ' . $target['width'] . 'x' . $target['height'];
$command .= (!empty($target['padtop']) ? ' -padtop ' . $target['padtop'] : '');
$command .= (!empty($target['padbottom']) ? ' -padbottom ' . $target['padbottom'] : '');
$command .= (!empty($target['padleft']) ? ' -padleft ' . $target['padleft'] : '');
$command .= (!empty($target['padright']) ? ' -padright ' . $target['padright'] : '');
$command .= ' -acodec mp3 /var/videos/converted/target.flv 2>&1';
exec($command,$output,$status);
if($status == 0)
{
// Success
echo 'Woohoo!';
}
else
{
// Error. $output has the details
echo '<pre>',join('\n',$output),'</pre>';
}
}