3

実行するのが奇妙に難しいタスクがあります。簡単だと思いましたが、すべての努力が実を結びませんでした。

phpスクリプトにアップロードされたビデオをさまざまな形式(.avi、.mpg、.wmv、.movなど)から単一の.flv形式に変換しています。変換はうまく機能していますが、私が問題を抱えているのはビデオの解像度です。

これは私が現在実行しているコマンドです(PHP変数を使用):

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

$originalと$convertedの両方に、これらのファイルへのフルパスが含まれています。私の問題は、ソースが小さい場合でも、これが常に640x480に変換されることです(私が言っているように)。明らかに、これはビデオがダウンロードされるときのディスクスペースと帯域幅の浪費です。また、これは4:3以外のアスペクト比の入力ビデオを考慮していないため、16:9のビデオをアップロードすると「押しつぶされた」変換になります。

私がしなければならないことが3つあります:

  1. 元のビデオのアスペクト比を決定します。
  2. 4:3でない場合は、上部と下部を黒いバーで埋めます。
  3. オリジナルのいずれかの寸法が大きい場合、またはオリジナルの幅/高さに関連するアスペクト比が4:3の場合(640x480に近い方)、640x480に変換します。

いくつかのビデオを実行ffmpeg -iしましたが、元の解像度を見つけるための一貫した形式または場所が表示されません。それを理解できたら、「計算を行って」適切なサイズを見つけ、-padttop、-padbottomなどでアスペクト比を固定するためのパディングを指定できることがわかります。

4

3 に答える 3

9

Ok。私は完全に機能するソリューションを持っています。これは、同じことをしたいというこの質問を見つけた人のためのものです。私のコードはあまりエレガントではないかもしれませんが、それは仕事を成し遂げます。


FFMPEGの出力を取得する

まず、ffmpeg -iから出力を取得する必要がありましたが、それ自体が課題でした。私の他の質問に対する覇権の答えのおかげで、私はついに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>';
    }
}
于 2009-07-14T03:34:53.370 に答える
1

私は 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\(\)]+)"

幅と高さを取得すると、アスペクト比を計算できます。

注: 場合によっては、式が失敗する可能性があることを知っています。

于 2009-07-10T18:20:44.227 に答える