Proper command escaping

This commit is contained in:
Romain Neutron 2012-10-08 14:20:59 +02:00
commit 70ba5000c1
5 changed files with 96 additions and 74 deletions

View file

@ -17,6 +17,7 @@ use FFMpeg\Exception\RuntimeException;
use FFMpeg\Format\Audio; use FFMpeg\Format\Audio;
use FFMpeg\Format\Video; use FFMpeg\Format\Video;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
/** /**
* FFMpeg driver * FFMpeg driver
@ -122,14 +123,16 @@ class FFMpeg extends Binary
throw new LogicException('No file open'); throw new LogicException('No file open');
} }
$cmd = $this->binary $builder = ProcessBuilder::create(array(
. ' -i ' . escapeshellarg($this->pathfile) $this->binary,
. ' -vframes 1 -ss ' . $time '-i', $this->pathfile,
. ' -f image2 ' . escapeshellarg($output); '-vframes', '1', '-ss', $time,
'-f', 'image2', $output
));
$this->logger->addInfo(sprintf('FFmpeg executes command %s', $cmd)); $process = $builder->getProcess();
$process = new Process($cmd); $this->logger->addInfo(sprintf('FFmpeg executes command %s', $process->getCommandline()));
try { try {
$process->run(); $process->run();
@ -188,26 +191,33 @@ class FFMpeg extends Binary
*/ */
protected function encodeAudio(Audio $format, $outputPathfile) protected function encodeAudio(Audio $format, $outputPathfile)
{ {
$cmd = $this->binary $builder = ProcessBuilder::create(array(
. ' -y -i ' $this->binary,
. escapeshellarg($this->pathfile) '-y', '-i',
. ' ' . $format->getExtraParams() $this->pathfile,
. ' -threads ' . $this->threads '-threads', $this->threads,
. ' -ab ' . $format->getKiloBitrate() . 'k ' '-ab', $format->getKiloBitrate() . 'k ',
. ' ' . escapeshellarg($outputPathfile); ));
foreach ($format->getExtraParams() as $parameter) {
$builder->add($parameter);
}
if ($format instanceof Audio\Transcodable) { if ($format instanceof Audio\Transcodable) {
$cmd .= ' -acodec ' . $format->getAudioCodec(); $builder->add('-acodec')->add($format->getAudioCodec());
} }
if ($format instanceof Audio\Resamplable) { if ($format instanceof Audio\Resamplable) {
$cmd .= ' -ac 2 -ar ' . $format->getAudioSampleRate(); $builder->add('-ac')->add(2)->add('-ar')->add($format->getAudioSampleRate());
} }
$process = new Process($cmd); $builder->add($outputPathfile);
$this->logger->addInfo(sprintf('FFmpeg executes command %s', $cmd)); $process = $builder->getProcess();
$this->logger->addInfo(sprintf('FFmpeg executes command %s', $process->getCommandLine()));
echo $process->getCommandLine() . "\n\n";
try { try {
$process->run(); $process->run();
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
@ -234,12 +244,14 @@ class FFMpeg extends Binary
*/ */
protected function encodeVideo(Video $format, $outputPathfile) protected function encodeVideo(Video $format, $outputPathfile)
{ {
$cmd_part1 = $this->binary $builder = ProcessBuilder::create(array(
. ' -y -i ' $this->binary, '-y', '-i',
. escapeshellarg($this->pathfile) . ' ' $this->pathfile
. $format->getExtraParams() . ' '; ));
$cmd_part2 = ''; foreach ($format->getExtraParams() as $parameter) {
$builder->add($parameter);
}
if ($format instanceof Video\Resizable) { if ($format instanceof Video\Resizable) {
if (!$this->prober) { if (!$this->prober) {
@ -275,54 +287,62 @@ class FFMpeg extends Binary
$width = $this->getMultiple($dimensions->getWidth(), 16); $width = $this->getMultiple($dimensions->getWidth(), 16);
$height = $this->getMultiple($dimensions->getHeight(), 16); $height = $this->getMultiple($dimensions->getHeight(), 16);
$cmd_part2 .= ' -s ' . $width . 'x' . $height; $builder->add('-s')->add($width . 'x' . $height);
} }
} }
if ($format instanceof Video\Resamplable) { if ($format instanceof Video\Resamplable) {
$cmd_part2 .= ' -r ' . $format->getFrameRate(); $builder->add('-r')->add($format->getFrameRate());
/** /**
* @see http://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping * @see http://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
*/ */
if ($format->supportBFrames()) { if ($format->supportBFrames()) {
$cmd_part2 .= ' -b_strategy 1 -bf 3 -g ' . $format->getGOPSize(); $builder->add('-b_strategy')
->add('1')
->add('-bf')
->add('3')
->add('-g')
->add($format->getGOPSize());
} }
} }
if ($format instanceof Video\Transcodable) { if ($format instanceof Video\Transcodable) {
$cmd_part2 .= ' -vcodec ' . $format->getVideoCodec(); $builder->add('-vcodec')->add($format->getVideoCodec());
} }
$cmd_part2 .= ' -b ' . $format->getKiloBitrate() . 'k' $builder->add('-b')->add($format->getKiloBitrate() . 'k')
. ' -threads ' . $this->threads ->add('-threads')->add($this->threads)
. ' -refs 6 -coder 1 -qmin 10 -qmax 51 ' ->add('-refs')->add('6')->add('-coder')->add('1')->add('-qmin')
. ' -sc_threshold 40 -flags +loop -cmp +chroma' ->add('10')->add('-qmax')->add('51')
. ' -me_range 16 -subq 7 -i_qfactor 0.71 -qcomp 0.6 -qdiff 4 ' ->add('-sc_threshold')->add('40')->add('-flags')->add('+loop')
. ' -trellis 1 -qscale 1 ' ->add('-cmp')->add('+chroma')
. ' -ab 92k '; ->add('-me_range')->add('16')->add('-subq')->add('7')
->add('-i_qfactor')->add('0.71')->add('-qcomp')->add('0.6')
->add('-qdiff')->add('4')
->add('-trellis')->add('1')->add('-qscale')->add('1')
->add('-ab')->add('92k');
if ($format instanceof Audio\Transcodable) { if ($format instanceof Audio\Transcodable) {
$cmd_part2 .= '-acodec ' . $format->getAudioCodec(); $builder->add('-acodec')->add($format->getAudioCodec());
} }
$tmpFile = new \SplFileInfo(tempnam(sys_get_temp_dir(), 'temp') . '.' . pathinfo($outputPathfile, PATHINFO_EXTENSION)); $tmpFile = new \SplFileInfo(tempnam(sys_get_temp_dir(), 'temp') . '.' . pathinfo($outputPathfile, PATHINFO_EXTENSION));
$passes = array(); $pass1 = $builder;
$pass2 = clone $builder;
$passes[] = $cmd_part1 . ' -pass 1 ' . $cmd_part2 $passes[] = $pass1
. ' -an ' . escapeshellarg($tmpFile->getPathname()); ->add('-pass')->add('1')->add('-an')->add($tmpFile->getPathname())
->getProcess();
$passes[] = $pass2
->add('-pass')->add('2')->add('-ac')->add('2')
->add('-ar')->add('44100')->add($outputPathfile)
->getProcess();
$passes[] = $cmd_part1 . ' -pass 2 ' . $cmd_part2 foreach ($passes as $process) {
. ' -ac 2 -ar 44100 ' . escapeshellarg($outputPathfile);
$process = null; $this->logger->addInfo(sprintf('FFmpeg executes command %s', $process->getCommandline()));
foreach ($passes as $pass) {
$this->logger->addInfo(sprintf('FFmpeg executes command %s', $pass));
$process = new Process($pass);
try { try {
$process->run(); $process->run();

View file

@ -14,6 +14,7 @@ namespace FFMpeg;
use FFMpeg\Exception\InvalidArgumentException; use FFMpeg\Exception\InvalidArgumentException;
use FFMpeg\Exception\RuntimeException; use FFMpeg\Exception\RuntimeException;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
/** /**
* FFProbe driver * FFProbe driver
@ -38,9 +39,11 @@ class FFProbe extends Binary
throw new InvalidArgumentException($pathfile); throw new InvalidArgumentException($pathfile);
} }
$cmd = $this->binary . ' ' . escapeshellarg($pathfile) . ' -show_format'; $builder = ProcessBuilder::create(array(
$this->binary, $pathfile, '-show_format'
));
$output = $this->executeProbe($cmd); $output = $this->executeProbe($builder->getProcess());
$ret = array(); $ret = array();
@ -84,9 +87,11 @@ class FFProbe extends Binary
throw new InvalidArgumentException($pathfile); throw new InvalidArgumentException($pathfile);
} }
$cmd = $this->binary . ' ' . escapeshellarg($pathfile) . ' -show_streams'; $builder = ProcessBuilder::create(array(
$this->binary, $pathfile, '-show_streams'
));
$output = explode("\n", $this->executeProbe($cmd)); $output = explode("\n", $this->executeProbe($builder->getProcess()));
$ret = array(); $ret = array();
$n = 0; $n = 0;
@ -123,28 +128,26 @@ class FFProbe extends Binary
/** /**
* *
* @param string $command * @param Process $process
* @return string * @return string
* @throws RuntimeException * @throws RuntimeException
*/ */
protected function executeProbe($command) protected function executeProbe(Process $process)
{ {
$this->logger->addInfo(sprintf('FFprobe executes command %s', $command)); $this->logger->addInfo(sprintf('FFprobe executes command %s', $process->getCommandline()));
try { try {
$process = new Process($command);
$process->run(); $process->run();
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
$this->logger->addInfo('FFprobe command failed'); $this->logger->addInfo('FFprobe command failed');
throw new RuntimeException(sprintf('Failed to run the given command %s', $command)); throw new RuntimeException(sprintf('Failed to run the given command %s', $process->getCommandline()));
} }
if ( ! $process->isSuccessful()) { if ( ! $process->isSuccessful()) {
$this->logger->addInfo('FFprobe command failed'); $this->logger->addInfo('FFprobe command failed');
throw new RuntimeException(sprintf('Failed to probe %s', $command)); throw new RuntimeException(sprintf('Failed to probe %s', $process->getCommandline()));
} }
$this->logger->addInfo('FFprobe command successful'); $this->logger->addInfo('FFprobe command successful');

View file

@ -27,10 +27,9 @@ interface Audio
public function getKiloBitrate(); public function getKiloBitrate();
/** /**
* Give som extra parameters to add to ffmpeg commandline * Return an array of extra parameters to add to ffmpeg commandline
* Parameters MUST be escaped
* *
* @return string * @return array()
*/ */
public function getExtraParams(); public function getExtraParams();

View file

@ -31,7 +31,7 @@ abstract class DefaultAudio implements Resamplable, Interactive
*/ */
public function getExtraParams() public function getExtraParams()
{ {
return ''; return array();
} }
/** /**

View file

@ -34,7 +34,7 @@ class WebM extends DefaultVideo
*/ */
public function getExtraParams() public function getExtraParams()
{ {
return '-f webm'; return array('-f', 'webm');
} }
/** /**