代码之家  ›  专栏  ›  技术社区  ›  Andrew Ensley

php和ffmpeg-执行智能视频转换

  •  4
  • Andrew Ensley  · 技术社区  · 15 年前

    我有一项很难完成的任务。我以为这很容易,但我所有的努力都没有结果。

    我正在将上传到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 在一些视频上,但我看不到一致的格式或位置来查找原始分辨率。一旦我知道了这一点,我就可以“计算”出正确的大小,并指定填充来固定长宽比-padtop、-padboom等。

    3 回复  |  直到 12 年前
        1
  •  3
  •   Evert    15 年前

    这对我很有用:

    $data = 'ffmpeg output';
    $matches = array();
    
    if (!preg_match('/Stream #(?:[0-9\.]+)(?:.*)\: Video: (?P<videocodec>.*) (?P<width>[0-9]*)x(?P<height>[0-9]*)/',$data,$matches)
       preg_match('/Could not find codec parameters \(Video: (?P<videocodec>.*) (?P<width>[0-9]*)x(?P<height>[0-9]*)\)/',$data,$matches)
    

    这可能不会 总是 工作,但大多数时候都是有效的,这在我看来已经足够好了:)

        2
  •  11
  •   Community CDub    7 年前

    好啊。我有一个功能齐全的解决方案。这是为任何人谁发现这个问题想做同样的事情。我的代码可能不太优雅,但它可以完成任务。


    获取ffmpeg的输出

    首先,我必须从ffmpeg-i获得输出,这本身就是一个挑战。多亏黑格尔蒙的回答 my other question ,我终于能够让它与 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;
    }
    

    确定最佳尺寸

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

    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;
    }
    

    用法

    下面是一些你将从中得到什么的例子 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)
    

    成品

    现在,把它们放在一起:

    $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>';
        }
    }
    
        3
  •  1
  •   Oleks    15 年前

    我不熟悉PHP,但几个月前我写了一个实用程序来使用C语言中的ffmpeg。我用正则表达式来做这个。有一些正则表达式可以帮助您:

    // this is for version detection
    "FFmpeg version (?<version>(\w|\d|\.|-)+)"
    // this is for duration parsing
    "Duration: (?<hours>\d{1,3}):(?<minutes>\d{2}):(?<seconds>\d{2})(.(?<fractions>\d{1,3}))?"
    
    // these are connected:
    // 0) this is base for getting stream info
    "Stream #(?<number>\d+?\.\d+?)(\((?<language>\w+)\))?: (?<type>.+): (?<data>.+)"
    // 1) if the type is audio:
    "(?<codec>\w+), (?<frequency>[\d]+) (?<frequencyUnit>[MK]?Hz), (?<chanel>\w+), (?<format>\w+)(, (?<bitrate>\d+) (?<bitrateUnit>[\w/]+))?"
    // 2) if the type is video:
    "(?<codec>\w+), (?<format>\w+), (?<width>\d+)x(?<height>\d+), (?<bitrate>\d+(\.\d+)?) (?<bitrateUnit>[\w\(\)]+)"
    

    因此,获得宽度和高度可以计算纵横比。

    注意:我知道在某些情况下表达式可能会失败。