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

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

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

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

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

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

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

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

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

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

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

Это может не всегда работать, но это работает большую часть времени, что было достаточно хорошо в моем случае 🙂

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


Получение вывода 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) 

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

Теперь, чтобы собрать все это вместе:

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

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

Получая ширину и высоту, вы можете рассчитать соотношение сторон.

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