Domanda

Ho un compito stranamente difficile da eseguire. Ho pensato che sarebbe stato facile, ma tutti i miei sforzi sono stati infruttuosi.

Sono la conversione video caricati su uno script php da vari formati (.avi, .mpg, .wmv, .mov, ecc) per un unico formato .flv. La conversione sta lavorando molto, ma quello che sto avendo problemi con è la risoluzione del video.

Questo è il comando Sono attualmente in esecuzione (con PHP vars):

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

Sia $ originale e $ convertito contengono i percorsi completi a tali file. Il mio problema è che questo converte sempre a 640x480 (come sto dicendo a) anche quando la fonte è più piccolo. Ovviamente, questo è uno spreco di spazio su disco e la larghezza di banda quando il video viene scaricato. Inoltre, questo non tiene conto ingressi video essere in qualsiasi formato diverso da 4: 3, risultante in una conversione "schiacciata" se carico 16:. Video 9

Ci sono 3 cose che devo fare:

  1. Determinare il formato del video originale.
  2. Se non 4: 3, in alto e in basso pad con barre nere
  3. .
  4. Conversione di 640x480 se una dimensione originale è più grande o un 4:. 3 aspect ratio relativo alla larghezza / altezza originale (qualunque sia più vicina a 640x480)

Ho eseguito ffmpeg -i su alcuni video, ma non vedo un formato coerente o posizione per trovare la risoluzione del originale. Una volta che sono in grado di capirlo, so di poter "fare i conti" per capire la giusta dimensione e specificare imbottitura per risolvere il rapporto di aspetto con -padttop, -padbottom, ecc.

È stato utile?

Soluzione

Questo funziona per me:

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

Questo non potrebbe sempre di lavoro, ma funziona la maggior parte delle volte, che era abbastanza buono nel mio caso:)

Altri suggerimenti

Ok. Ho una soluzione pienamente funzionale. Questo è per chi trova questa domanda che vogliono fare la stessa cosa. Il mio codice non può essere molto elegante, ma ottiene il lavoro fatto.


Come uscita di FFmpeg

Per prima cosa ho dovuto ottenere l'output di ffmpeg -i, che era una sfida in sé. Grazie alla risposta di egemone su mia altra domanda , sono stato finalmente in grado di farlo funzionare con 2>&1 alla fine del mio comando. E grazie alla risposta di Evert a questa domanda, sono stato in grado di analizzare l'uscita con preg_match per trovare l'altezza e la larghezza del file originale.

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

determinare le migliori dimensioni

Ho scritto questa funzione per determinare le migliori dimensioni da utilizzare per la conversione. Prende dimensioni della originali, dimensioni di destinazione, e se non forzare la conversione in formato di destinazione (determinata dalla larghezza / altezza). Le dimensioni di destinazione sarà sempre il più grande risultato questa funzione può restituire.

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

Uso

Ecco alcuni esempi di ciò che si ottengono da get_dimensions() per rendere le cose più chiaro:

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)

Il prodotto finito

Ora, per mettere tutto insieme:

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

Non ho familiarità con PHP, ma ho scritto un programma di utilità per lavorare con ffmpeg in C # diversi mesi fa. Ho usato le espressioni regolari per fare questo. Ci sono alcune espressioni regolari che possono aiutare da lì:

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

In modo da ottenere larghezza e l'altezza si può calcolare un rapporto di aspetto.

Nota: So che in alcuni casi ci espressioni possono fallire

.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top