PHP и FFMPEG - Выполнение интеллектуального преобразования видео - PullRequest
4 голосов
/ 10 июля 2009

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

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

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

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

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

Мне нужно сделать 3 вещи:

  1. Определите соотношение сторон исходного видео.
  2. Если не 4: 3, сверху и снизу панели с черными полосами.
  3. Конвертировать в 640x480, если размер оригинала больше или формат изображения 4: 3, относящийся к ширине / высоте оригинала (в зависимости от того, что ближе к 640x480).

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

Ответы [ 3 ]

10 голосов
/ 14 июля 2009

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


Получение вывода FFMPEG

Сначала я должен был получить вывод от 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;
}

Определение лучших размеров

Я написал эту функцию, чтобы определить наилучшие размеры для конвертации. Требуются размеры оригинала, целевые размеры и необходимость принудительного преобразования в целевое соотношение сторон (определяется по его ширине / высоте). Целевые измерения всегда будут наибольшим результатом, который может вернуть эта функция.

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)

Готовый продукт

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

<code>$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),'
'; } }
3 голосов
/ 10 июля 2009

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

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

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

1 голос
/ 10 июля 2009

Я не знаком с 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\(\)]+)"

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

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

...