PHP和FFMPEG - 执行智能视频转换

And*_*ley 4 php ffmpeg video-processing

我有一项奇怪的艰巨任务.我觉得这很容易,但我所有的努力都没有结果.

我正在将上传到php脚本的视频从各种格式(.avi,.mpg,.wmv,.mov等)转换为单个.flv格式.转换效果很好,但我遇到的问题是视频的分辨率.

这是我正在运行的命令(使用PHP vars):

ffmpeg -i $original -ab 96k -b 700k -ar 44100 -s 640x480 -acodec mp3 $converted

$ original和$ converted都包含这些文件的完整路径.我的问题是,即使源较小,它总是转换为640x480(就像我告诉它).显然,这是在下载视频时浪费磁盘空间和带宽.此外,这并不考虑输入视频是否为4:3以外的任何宽高比,如果我上传16:9视频,则会导致"压缩"转换.

我需要做三件事:

  1. 确定原始视频的宽高比.
  2. 如果不是4:3,则用黑色条填充顶部和底部.
  3. 如果原件的尺寸较大或与原件的宽度/高度相关的4:3宽高比(以较接近640x480为准),则转换为640x480.

我已经播放ffmpeg -i了一些视频,但是我没有看到一致的格式或位置来查找原始分辨率.一旦我能够弄清楚,我知道我可以"做数学"来找出正确的大小并指定填充以使用-padttop,-padbottom等来修复宽高比.

And*_*ley 10

好.我有一个功能齐全的解决方案.这适用于任何发现此问题想要做同样事情的人.我的代码可能不是很优雅,但它完成了工作.


获得FFMPEG的输出

首先,我必须从ffmpeg -i获得输出,这本身就是一个挑战.感谢hegemon对我的另一个问题的回答,我终于能够2>&1在我的命令结束时使用它.感谢Evert对这个问题的回答,我能够解析输出,preg_match找到原始文件的高度和宽度.

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;
}
Run Code Online (Sandbox Code Playgroud)

确定最佳尺寸

我写了这个函数来确定用于转换的最佳尺寸.它采用原始尺寸,目标尺寸以及是否强制转换为目标纵横比(根据其宽度/高度确定).目标尺寸始终是此函数可返回的最大结果.

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;
}
Run Code Online (Sandbox Code Playgroud)

用法

以下是您将从中获得get_dimensions()更清晰的内容的几个示例:

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)
Run Code Online (Sandbox Code Playgroud)

成品

现在,把它们放在一起:

$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>';
    }
}
Run Code Online (Sandbox Code Playgroud)