Add resize mode, move getMultiple method to FFMpeg
This commit is contained in:
parent
1424af7703
commit
fc7529822f
6 changed files with 185 additions and 62 deletions
|
|
@ -27,6 +27,18 @@ class FFMpeg extends Binary
|
||||||
{
|
{
|
||||||
protected $pathfile;
|
protected $pathfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var FFProbe
|
||||||
|
*/
|
||||||
|
protected $prober;
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
$this->prober = null;
|
||||||
|
parent::__destruct();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a file in order to be processed
|
* Opens a file in order to be processed
|
||||||
*
|
*
|
||||||
|
|
@ -49,6 +61,18 @@ class FFMpeg extends Binary
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a prober
|
||||||
|
*
|
||||||
|
* @return \FFMpeg\FFMpeg
|
||||||
|
*/
|
||||||
|
public function setProber(FFProbe $prober)
|
||||||
|
{
|
||||||
|
$this->prober = $prober;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close a file
|
* Close a file
|
||||||
*
|
*
|
||||||
|
|
@ -89,7 +113,7 @@ class FFMpeg extends Binary
|
||||||
try {
|
try {
|
||||||
$process->run();
|
$process->run();
|
||||||
} catch (\RuntimeException $e) {
|
} catch (\RuntimeException $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $process->isSuccessful()) {
|
if ( ! $process->isSuccessful()) {
|
||||||
|
|
@ -164,7 +188,7 @@ class FFMpeg extends Binary
|
||||||
try {
|
try {
|
||||||
$process->run();
|
$process->run();
|
||||||
} catch (\RuntimeException $e) {
|
} catch (\RuntimeException $e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $process->isSuccessful()) {
|
if ( ! $process->isSuccessful()) {
|
||||||
|
|
@ -196,7 +220,43 @@ class FFMpeg extends Binary
|
||||||
$cmd_part2 = '';
|
$cmd_part2 = '';
|
||||||
|
|
||||||
if ($format->getWidth() && $format->getHeight()) {
|
if ($format->getWidth() && $format->getHeight()) {
|
||||||
$cmd_part2 .= ' -s ' . $format->getWidth() . 'x' . $format->getHeight();
|
|
||||||
|
switch ($format->getResizeMode()) {
|
||||||
|
case Video::RESIZEMODE_FIT:
|
||||||
|
default:
|
||||||
|
$width = $this->getMultiple($format->getWidth(), 16);
|
||||||
|
$height = $this->getMultiple($format->getHeight(), 16);
|
||||||
|
break;
|
||||||
|
case Video::RESIZEMODE_INSET:
|
||||||
|
|
||||||
|
if ( ! $this->prober) {
|
||||||
|
throw new LogicException('You must set a valid prober if you use RESIZEMODE_INSET');
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->prober->probeStreams($this->pathfile);
|
||||||
|
|
||||||
|
$originalWidth = $format->getWidth();
|
||||||
|
$originalHeight = $format->getHeight();
|
||||||
|
|
||||||
|
foreach ($result as $stream) {
|
||||||
|
foreach ($stream as $info) {
|
||||||
|
if (strpos($info, 'width=') === 0) {
|
||||||
|
$originalWidth = substr($info, 6);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strpos($info, 'height=') === 0) {
|
||||||
|
$originalHeight = substr($info, 7);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$width = $this->getMultiple($originalWidth, 16);
|
||||||
|
$height = $this->getMultiple($originalHeight, 16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd_part2 .= ' -s ' . $width . 'x' . $height;
|
||||||
}
|
}
|
||||||
|
|
||||||
$cmd_part2 .= ' -r ' . $format->getFrameRate()
|
$cmd_part2 .= ' -r ' . $format->getFrameRate()
|
||||||
|
|
@ -260,6 +320,42 @@ class FFMpeg extends Binary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nearest multiple for a value
|
||||||
|
*
|
||||||
|
* @param integer $value
|
||||||
|
* @param integer $multiple
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
protected function getMultiple($value, $multiple)
|
||||||
|
{
|
||||||
|
$modulo = $value % $multiple;
|
||||||
|
|
||||||
|
$ret = (int) $multiple;
|
||||||
|
|
||||||
|
$halfDistance = $multiple / 2;
|
||||||
|
if ($modulo <= $halfDistance)
|
||||||
|
$bound = 'bottom';
|
||||||
|
else
|
||||||
|
$bound = 'top';
|
||||||
|
|
||||||
|
switch ($bound) {
|
||||||
|
default:
|
||||||
|
case 'top':
|
||||||
|
$ret = $value + $multiple - $modulo;
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
$ret = $value - $modulo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ret < $multiple) {
|
||||||
|
$ret = (int) $multiple;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,23 @@ class FFProbe extends Binary
|
||||||
|
|
||||||
$cmd = $this->binary . ' ' . $pathfile . ' -show_streams';
|
$cmd = $this->binary . ' ' . $pathfile . ' -show_streams';
|
||||||
|
|
||||||
return $this->executeProbe($cmd);
|
$output = explode("\n", $this->executeProbe($cmd));
|
||||||
|
|
||||||
|
$ret = array();
|
||||||
|
$n = 0;
|
||||||
|
|
||||||
|
foreach ($output as $line) {
|
||||||
|
|
||||||
|
if (in_array($line, array('[STREAM]', '[/STREAM]'))) {
|
||||||
|
$n ++;
|
||||||
|
$ret[$n] = array();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret[$n][] = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,11 @@ use FFMpeg\Exception\InvalidArgumentException;
|
||||||
*/
|
*/
|
||||||
abstract class DefaultVideo extends DefaultAudio implements Video
|
abstract class DefaultVideo extends DefaultAudio implements Video
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $width;
|
protected $width;
|
||||||
protected $height;
|
protected $height;
|
||||||
protected $frameRate = 25;
|
protected $frameRate = 25;
|
||||||
|
protected $resizeMode = self::RESIZEMODE_FIT;
|
||||||
protected $videoCodec;
|
protected $videoCodec;
|
||||||
protected $GOPsize = 25;
|
protected $GOPsize = 25;
|
||||||
protected $kiloBitrate = 1000;
|
protected $kiloBitrate = 1000;
|
||||||
|
|
@ -59,12 +61,42 @@ abstract class DefaultVideo extends DefaultAudio implements Video
|
||||||
throw new InvalidArgumentException('Wrong height value');
|
throw new InvalidArgumentException('Wrong height value');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->width = $this->getMultiple($width, 16);
|
$this->width = $width;
|
||||||
$this->height = $this->getMultiple($height, 16);
|
$this->height = $height;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resize mode
|
||||||
|
*
|
||||||
|
* @param string $mode The mode, one of the self::RESIZEMODE_* constants
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function setResizeMode($mode)
|
||||||
|
{
|
||||||
|
if ( ! in_array($mode, array(self::RESIZEMODE_FIT, self::RESIZEMODE_INSET))) {
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
'Resize mode `%s` is not valid , avalaible values are %s',
|
||||||
|
$mode,
|
||||||
|
implode(', ', array(self::RESIZEMODE_FIT, self::RESIZEMODE_INSET))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->resizeMode = $mode;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getResizeMode()
|
||||||
|
{
|
||||||
|
return $this->resizeMode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -145,40 +177,4 @@ abstract class DefaultVideo extends DefaultAudio implements Video
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the nearest multiple for a value
|
|
||||||
*
|
|
||||||
* @param integer $value
|
|
||||||
* @param integer $multiple
|
|
||||||
* @return integer
|
|
||||||
*/
|
|
||||||
protected function getMultiple($value, $multiple)
|
|
||||||
{
|
|
||||||
$modulo = $value % $multiple;
|
|
||||||
|
|
||||||
$ret = (int) $multiple;
|
|
||||||
|
|
||||||
$halfDistance = $multiple / 2;
|
|
||||||
if ($modulo <= $halfDistance)
|
|
||||||
$bound = 'bottom';
|
|
||||||
else
|
|
||||||
$bound = 'top';
|
|
||||||
|
|
||||||
switch ($bound) {
|
|
||||||
default:
|
|
||||||
case 'top':
|
|
||||||
$ret = $value + $multiple - $modulo;
|
|
||||||
break;
|
|
||||||
case 'bottom':
|
|
||||||
$ret = $value - $modulo;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ret < $multiple) {
|
|
||||||
$ret = (int) $multiple;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) $ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ namespace FFMpeg\Format;
|
||||||
*/
|
*/
|
||||||
interface Video extends Audio
|
interface Video extends Audio
|
||||||
{
|
{
|
||||||
|
const RESIZEMODE_FIT = 'fit';
|
||||||
|
const RESIZEMODE_INSET = 'inset';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the width
|
* Returns the width
|
||||||
|
|
@ -33,6 +35,13 @@ interface Video extends Audio
|
||||||
*/
|
*/
|
||||||
public function getHeight();
|
public function getHeight();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current resize mode
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getResizeMode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the frame rate
|
* Returns the frame rate
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class FFMpegTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
$this->object = FFMpeg::load($this->logger);
|
$this->object = FFMpeg::load($this->logger);
|
||||||
$this->probe = FFProbe::load($this->logger);
|
$this->probe = FFProbe::load($this->logger);
|
||||||
|
$this->object->setProber($this->probe);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -119,6 +120,7 @@ class FFMpegTest extends \PHPUnit_Framework_TestCase
|
||||||
$logger->pushHandler(new \Monolog\Handler\NullHandler());
|
$logger->pushHandler(new \Monolog\Handler\NullHandler());
|
||||||
|
|
||||||
$ffmpeg = new FFMpeg('wrongbinary', $logger);
|
$ffmpeg = new FFMpeg('wrongbinary', $logger);
|
||||||
|
$ffmpeg->setProber($this->probe);
|
||||||
$ffmpeg->open(__DIR__ . '/../../files/Test.ogv');
|
$ffmpeg->open(__DIR__ . '/../../files/Test.ogv');
|
||||||
|
|
||||||
$format = new Format\Video\WebM();
|
$format = new Format\Video\WebM();
|
||||||
|
|
@ -215,4 +217,26 @@ class FFMpegTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
unlink($dest);
|
unlink($dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers FFMpeg\FFMpeg::getMultiple
|
||||||
|
*/
|
||||||
|
public function testGetMultiple()
|
||||||
|
{
|
||||||
|
$object = FFMpegTester::load($this->logger);
|
||||||
|
$this->assertEquals(320, $object->getMultipleTester(321, 16));
|
||||||
|
$this->assertEquals(320, $object->getMultipleTester(319, 16));
|
||||||
|
$this->assertEquals(320, $object->getMultipleTester(313, 16));
|
||||||
|
$this->assertEquals(304, $object->getMultipleTester(312, 16));
|
||||||
|
$this->assertEquals(336, $object->getMultipleTester(329, 16));
|
||||||
|
$this->assertEquals(16, $object->getMultipleTester(8, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FFMpegTester extends FFMpeg
|
||||||
|
{
|
||||||
|
public function getMultipleTester($value, $multiple)
|
||||||
|
{
|
||||||
|
return parent::getMultiple($value, $multiple);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ class DefaultVideoTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(640, $this->object->getHeight());
|
$this->assertEquals(640, $this->object->getHeight());
|
||||||
|
|
||||||
$this->object->setDimensions(242, 638);
|
$this->object->setDimensions(242, 638);
|
||||||
$this->assertEquals(240, $this->object->getWidth());
|
$this->assertEquals(242, $this->object->getWidth());
|
||||||
$this->assertEquals(640, $this->object->getHeight());
|
$this->assertEquals(638, $this->object->getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -155,19 +155,6 @@ class DefaultVideoTest extends \PHPUnit_Framework_TestCase
|
||||||
return array(array(-5), array(0));
|
return array(array(-5), array(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers FFMpeg\Format\DefaultVideo::getMultiple
|
|
||||||
*/
|
|
||||||
public function testGetMultiple()
|
|
||||||
{
|
|
||||||
$this->assertEquals(320, $this->object->getMultiple(321, 16));
|
|
||||||
$this->assertEquals(320, $this->object->getMultiple(319, 16));
|
|
||||||
$this->assertEquals(320, $this->object->getMultiple(313, 16));
|
|
||||||
$this->assertEquals(304, $this->object->getMultiple(312, 16));
|
|
||||||
$this->assertEquals(336, $this->object->getMultiple(329, 16));
|
|
||||||
$this->assertEquals(16, $this->object->getMultiple(8, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers FFMpeg\Format\DefaultVideo::getKiloBitrate
|
* @covers FFMpeg\Format\DefaultVideo::getKiloBitrate
|
||||||
*/
|
*/
|
||||||
|
|
@ -199,9 +186,4 @@ class DefaultVideoTester extends DefaultVideo
|
||||||
return '-f format';
|
return '-f format';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMultiple($value, $multiple)
|
|
||||||
{
|
|
||||||
return parent::getMultiple($value, $multiple);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue