PHP и FFMPEG - Выполнение интеллектуального преобразования видео

У меня странно сложная задача. Я думал, что это будет легко, но все мои усилия оказались бесплодными.

Я преобразую видео, загруженные в php-скрипт из различных форматов (.avi, .mpg, .wmv, .mov и т. Д.), В один формат .flv. Преобразование работает отлично, но у меня проблемы с разрешением видео.

Это команда, которую я сейчас выполняю (с PHP-вариациями):

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

И $ original, и $ convert содержат полные пути к этим файлам. Моя проблема в том, что это всегда преобразуется в 640x480 (как я говорю), даже когда источник меньше. Очевидно, что при загрузке видео это пустая трата дискового пространства и пропускной способности. Кроме того, это не учитывает входные видео в любом соотношении сторон, кроме 4: 3, что приводит к «сжатому» формату. преобразование, если я загружаю видео 16: 9.

Есть 3 вещи, которые мне нужно сделать:

Determine the aspect ratio of the original video. If not 4:3, pad top and bottom with black bars. Convert to 640x480 if either dimension of the original is larger or a 4:3 aspect ratio relating to the width/height of the original (whichever is closer to 640x480).

Я бегуffmpeg -i на нескольких видео, но я не вижу согласованного формата или местоположения, в котором можно найти исходное разрешение. Как только я смогу это выяснить, я знаю, что смогу «сделать математику». чтобы определить правильный размер и указать отступы, чтобы исправить соотношение сторон с помощью -padttop, -padbottom и т. д.

Ответы на вопрос(3)

Я не знаком с PHP, но я написал утилиту для работы с ffmpeg на C # несколько месяцев назад. Я использовал регулярные выражения, чтобы сделать это. Вот некоторые регулярные выражения, которые могут вам помочь:

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

Таким образом, получая ширину и высоту, вы можете рассчитать соотношение сторон.

Примечание: я знаю, что в некоторых случаях выражения могут потерпеть неудачу.

 10 июл. 2009 г., 20:35
На самом деле я могу опубликовать свой собственный код для этого, написанный на c #, если хотите, но думаю, что это не поможет.
 Andrew Ensley10 июл. 2009 г., 20:29
Благодарю. Я тоже попробую, как только смогу на самом деле захватить вывод ffmpeg. :-)
Решение Вопроса

Это работает для меня:

$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)

Это не можетalways работать, но это работает в большинстве случаев, что было достаточно хорошо в моем случае :)

 Andrew Ensley10 июл. 2009 г., 07:21
Благодарю. Я попробую.
 Andrew Ensley14 июл. 2009 г., 05:35
В случае, если вы заинтересованы, я наконец собрал все части вместе и опубликовал мой ответ для справки.stackoverflow.com/questions/1106955/…
 Andrew Ensley10 июл. 2009 г., 21:23
Это работает для каждого видео, которое я тестировал до сих пор. Как только у меня будет надежное решение, я опубликую свой код в качестве ответа на тот случай, если он понадобится всем, кто заходит на эту страницу. Благодарю.

Хорошо. У меня есть полнофункциональное решение. Это для тех, кто считает этот вопрос желающим сделать то же самое. Мой код может быть не очень элегантным, но он выполняет свою работу.

Getting FFMPEG's output

Сначала я должен был получить вывод из ffmpeg -i, что само по себе было проблемой. Благодаря ответу гегемона намой другой вопросЯ наконец-то смог заставить его работать с2>&1 в конце моей команды. И благодаря ответу Эверта на этот вопрос я смог разобрать вывод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;
}
Determining the best 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;
}
Usage

Вот несколько примеров того, что вы получите от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)
The Finished Product

Теперь, чтобы сложить все вместе:

$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>';
    }
}
 28 мар. 2011 г., 05:24
+1, я работал над тем же самым, спасибо за публикацию этого. знак равно
 13 февр. 2013 г., 00:03
Вы можете обновить свой ответ, чтобы отразить изменения в API ffmpeg.-padtop и т. д. были удалены в пользу подушечного фильтра, т.е.-vf "pad=<horizontal>:<vertical>:<left>:<right>:<colour>"
 Andrew Ensley06 окт. 2009 г., 06:30
Так что, похоже, по какой-то причине возникают проблемы с отображением этого ответа. Я разместил его на своем веб-сайте, если кому-то это интересно.andrewensley.com/2009/10/…

Ваш ответ на вопрос