/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Coordinate/AspectRatio.php |
@@ -0,0 +1,248 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <dev.team@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Coordinate; |
|
use FFMpeg\Exception\InvalidArgumentException; |
|
// see http://en.wikipedia.org/wiki/List_of_common_resolutions |
class AspectRatio |
{ |
// named 4:3 or 1.33:1 Traditional TV |
const AR_4_3 = '4/3'; |
// named 16:9 or 1.77:1 HD video standard |
const AR_16_9 = '16/9'; |
|
// named 3:2 or 1.5:1 see http://en.wikipedia.org/wiki/135_film |
const AR_3_2 = '3/2'; |
// named 5:3 or 1.66:1 see http://en.wikipedia.org/wiki/Super_16_mm |
const AR_5_3 = '5/3'; |
|
// mostly used in Photography |
const AR_5_4 = '5/4'; |
const AR_1_1 = '1/1'; |
|
// 1.85:1 US widescreen cinema standard see http://en.wikipedia.org/wiki/Widescreen#Film |
const AR_1_DOT_85_1 = '1.85:1'; |
// 2.39:1 or 2.40:1 Current widescreen cinema standard see http://en.wikipedia.org/wiki/Anamorphic_format |
const AR_2_DOT_39_1 = '2.39:1'; |
|
// Rotated constants |
|
// Rotated 4:3 |
const AR_ROTATED_3_4 = '3/4'; |
// Rotated 16:9 |
const AR_ROTATED_9_16 = '9/16'; |
|
// Rotated 3:2 |
const AR_ROTATED_2_3 = '2/3'; |
// Rotated 5:3 |
const AR_ROTATED_3_5 = '3/5'; |
|
// Rotated 5:4 |
const AR_ROTATED_4_5 = '4/5'; |
|
// Rotated 1.85 |
const AR_ROTATED_1_DOT_85 = '1/1.85'; |
// Rotated 2.39 |
const AR_ROTATED_2_DOT_39 = '1/2.39'; |
|
/** @var float */ |
private $ratio; |
|
public function __construct($ratio) |
{ |
$this->ratio = $ratio; |
} |
|
/** |
* Returns the value of the ratio. |
* |
* @return float |
*/ |
public function getValue() |
{ |
return $this->ratio; |
} |
|
/** |
* Computes the best width for given height and modulus. |
* |
* @param Integer $height |
* @param Integer $modulus |
* |
* @return Integer |
*/ |
public function calculateWidth($height, $modulus = 1) |
{ |
$maxPossibleWidth = $this->getMultipleUp(ceil($this->ratio * $height), $modulus); |
$minPossibleWidth = $this->getMultipleDown(floor($this->ratio * $height), $modulus); |
|
$maxRatioDiff = abs($this->ratio - ($maxPossibleWidth / $height)); |
$minRatioDiff = abs($this->ratio - ($minPossibleWidth / $height)); |
|
return $maxRatioDiff < $minRatioDiff ? $maxPossibleWidth : $minPossibleWidth; |
} |
|
/** |
* Computes the best height for given width and modulus. |
* |
* @param Integer $width |
* @param Integer $modulus |
* |
* @return Integer |
*/ |
public function calculateHeight($width, $modulus = 1) |
{ |
$maxPossibleHeight = $this->getMultipleUp(ceil($width / $this->ratio), $modulus); |
$minPossibleHeight = $this->getMultipleDown(floor($width / $this->ratio), $modulus); |
|
$maxRatioDiff = abs($this->ratio - ($width / $maxPossibleHeight)); |
$minRatioDiff = abs($this->ratio - ($width / $minPossibleHeight)); |
|
return $maxRatioDiff < $minRatioDiff ? $maxPossibleHeight : $minPossibleHeight; |
} |
|
private function getMultipleUp($value, $multiple) |
{ |
while (0 !== $value % $multiple) { |
$value++; |
} |
|
return $value; |
} |
|
private function getMultipleDown($value, $multiple) |
{ |
while (0 !== $value % $multiple) { |
$value--; |
} |
|
return $value; |
} |
|
/** |
* Creates a ratio based on Dimension. |
* |
* The strategy parameter forces by default to use standardized ratios. If |
* custom ratio need to be used, disable it. |
* |
* @param Dimension $dimension |
* @param Boolean $forceStandards Whether to force or not standard ratios |
* |
* @return AspectRatio |
* |
* @throws InvalidArgumentException |
*/ |
public static function create(Dimension $dimension, $forceStandards = true) |
{ |
$incoming = $dimension->getWidth() / $dimension->getHeight(); |
|
if ($forceStandards) { |
return new static(static::nearestStrategy($incoming)); |
} else { |
return new static(static::customStrategy($incoming)); |
} |
} |
|
private static function valueFromName($name) |
{ |
switch ($name) { |
case static::AR_4_3: |
return 4 / 3; |
case static::AR_16_9: |
return 16 / 9; |
case static::AR_1_1: |
return 1 / 1; |
case static::AR_1_DOT_85_1: |
return 1.85; |
case static::AR_2_DOT_39_1: |
return 2.39; |
case static::AR_3_2: |
return 3 / 2; |
case static::AR_5_3: |
return 5 / 3; |
case static::AR_5_4: |
return 5 / 4; |
case static::AR_ROTATED_3_4: |
return 3 / 4; |
case static::AR_ROTATED_9_16: |
return 9 / 16; |
case static::AR_ROTATED_2_3: |
return 2 / 3; |
case static::AR_ROTATED_3_5: |
return 3 / 5; |
case static::AR_ROTATED_4_5: |
return 4 / 5; |
case static::AR_ROTATED_1_DOT_85: |
return 1 / 1.85; |
case static::AR_ROTATED_2_DOT_39: |
return 1 / 2.39; |
default: |
throw new InvalidArgumentException(sprintf('Unable to find value for %s', $name)); |
} |
} |
|
private static function customStrategy($incoming) |
{ |
$try = static::nearestStrategy($incoming); |
|
if (abs($try - $incoming) < $try * 0.05) { |
return $try; |
} |
|
return $incoming; |
} |
|
private static function nearestStrategy($incoming) |
{ |
$availables = array( |
static::AR_4_3 => static::valueFromName(static::AR_4_3), |
static::AR_16_9 => static::valueFromName(static::AR_16_9), |
static::AR_1_1 => static::valueFromName(static::AR_1_1), |
static::AR_1_DOT_85_1 => static::valueFromName(static::AR_1_DOT_85_1), |
static::AR_2_DOT_39_1 => static::valueFromName(static::AR_2_DOT_39_1), |
static::AR_3_2 => static::valueFromName(static::AR_3_2), |
static::AR_5_3 => static::valueFromName(static::AR_5_3), |
static::AR_5_4 => static::valueFromName(static::AR_5_4), |
|
// Rotated |
static::AR_ROTATED_4_5 => static::valueFromName(static::AR_ROTATED_4_5), |
static::AR_ROTATED_9_16 => static::valueFromName(static::AR_ROTATED_9_16), |
static::AR_ROTATED_2_3 => static::valueFromName(static::AR_ROTATED_2_3), |
static::AR_ROTATED_3_5 => static::valueFromName(static::AR_ROTATED_3_5), |
static::AR_ROTATED_3_4 => static::valueFromName(static::AR_ROTATED_3_4), |
static::AR_ROTATED_1_DOT_85 => static::valueFromName(static::AR_ROTATED_1_DOT_85), |
static::AR_ROTATED_2_DOT_39 => static::valueFromName(static::AR_ROTATED_2_DOT_39), |
); |
asort($availables); |
|
$previous = $current = null; |
|
foreach ($availables as $name => $value) { |
$current = $value; |
if ($incoming <= $value) { |
break; |
} |
$previous = $value; |
} |
|
if (null === $previous) { |
return $current; |
} |
|
if (($current - $incoming) < ($incoming - $previous)) { |
return $current; |
} |
|
return $previous; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Coordinate/TimeCode.php |
@@ -0,0 +1,120 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Coordinate; |
|
use FFMpeg\Exception\InvalidArgumentException; |
|
class TimeCode |
{ |
//see http://www.dropframetimecode.org/ |
private $hours; |
private $minutes; |
private $seconds; |
private $frames; |
|
public function __construct($hours, $minutes, $seconds, $frames) |
{ |
$this->hours = $hours; |
$this->minutes = $minutes; |
$this->seconds = $seconds; |
$this->frames = $frames; |
} |
|
public function __toString() |
{ |
return sprintf('%02d:%02d:%02d.%02d', $this->hours, $this->minutes, $this->seconds, $this->frames); |
} |
|
/** |
* Creates timecode from string. |
* |
* @param string $timecode |
* |
* @return TimeCode |
* |
* @throws InvalidArgumentException In case an invalid timecode is supplied |
*/ |
public static function fromString($timecode) |
{ |
$days = 0; |
|
if (preg_match('/^[0-9]+:[0-9]+:[0-9]+:[0-9]+\.[0-9]+$/', $timecode)) { |
list($days, $hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d:%d.%d'); |
} elseif (preg_match('/^[0-9]+:[0-9]+:[0-9]+:[0-9]+:[0-9]+$/', $timecode)) { |
list($days, $hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d:%d:%d'); |
} elseif (preg_match('/^[0-9]+:[0-9]+:[0-9]+\.[0-9]+$/', $timecode)) { |
list($hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d.%s'); |
} elseif (preg_match('/^[0-9]+:[0-9]+:[0-9]+:[0-9]+$/', $timecode)) { |
list($hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d:%s'); |
} else { |
throw new InvalidArgumentException(sprintf('Unable to parse timecode %s', $timecode)); |
} |
|
$hours += $days * 24; |
|
return new static($hours, $minutes, $seconds, $frames); |
} |
|
/** |
* Creates timecode from number of seconds. |
* |
* @param float $quantity |
* |
* @return TimeCode |
*/ |
public static function fromSeconds($quantity) |
{ |
$minutes = $hours = $frames = 0; |
|
$frames = round(100 * ($quantity - floor($quantity))); |
$seconds = floor($quantity); |
|
if ($seconds > 59) { |
$minutes = floor($seconds / 60); |
$seconds = $seconds % 60; |
} |
if ($minutes > 59) { |
$hours = floor($minutes / 60); |
$minutes = $minutes % 60; |
} |
|
return new static($hours, $minutes, $seconds, $frames); |
} |
|
/** |
* Returns this timecode in seconds |
* @return int |
*/ |
public function toSeconds() { |
$seconds = 0; |
|
$seconds += $this->hours * 60 * 60; |
$seconds += $this->minutes * 60; |
$seconds += $this->seconds; |
|
// TODO: Handle frames? |
|
return (int) $seconds; |
} |
|
/** |
* Helper function wether `$timecode` is after this one |
* |
* @param TimeCode $timecode The Timecode to compare |
* @return bool |
*/ |
public function isAfter(TimeCode $timecode) { |
// convert everything to seconds and compare |
return ($this->toSeconds() > $timecode->toSeconds()); |
} |
|
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Driver/FFMpegDriver.php |
@@ -0,0 +1,57 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Driver; |
|
use Alchemy\BinaryDriver\AbstractBinary; |
use Alchemy\BinaryDriver\Configuration; |
use Alchemy\BinaryDriver\ConfigurationInterface; |
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException as BinaryDriverExecutableNotFound; |
use FFMpeg\Exception\ExecutableNotFoundException; |
use Psr\Log\LoggerInterface; |
|
class FFMpegDriver extends AbstractBinary |
{ |
/** |
* {@inheritdoc} |
*/ |
public function getName() |
{ |
return 'ffmpeg'; |
} |
|
/** |
* Creates an FFMpegDriver. |
* |
* @param LoggerInterface $logger |
* @param array|Configuration $configuration |
* |
* @return FFMpegDriver |
*/ |
public static function create(LoggerInterface $logger = null, $configuration = array()) |
{ |
if (!$configuration instanceof ConfigurationInterface) { |
$configuration = new Configuration($configuration); |
} |
|
$binaries = $configuration->get('ffmpeg.binaries', array('avconv', 'ffmpeg')); |
|
if (!$configuration->has('timeout')) { |
$configuration->set('timeout', 300); |
} |
|
try { |
return static::load($binaries, $logger, $configuration); |
} catch (BinaryDriverExecutableNotFound $e) { |
throw new ExecutableNotFoundException('Unable to load FFMpeg', $e->getCode(), $e); |
} |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFMpeg.php |
@@ -0,0 +1,122 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg; |
|
use Alchemy\BinaryDriver\ConfigurationInterface; |
use FFMpeg\Driver\FFMpegDriver; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Media\Audio; |
use FFMpeg\Media\Video; |
use Psr\Log\LoggerInterface; |
|
class FFMpeg |
{ |
/** @var FFMpegDriver */ |
private $driver; |
/** @var FFProbe */ |
private $ffprobe; |
|
public function __construct(FFMpegDriver $ffmpeg, FFProbe $ffprobe) |
{ |
$this->driver = $ffmpeg; |
$this->ffprobe = $ffprobe; |
} |
|
/** |
* Sets FFProbe. |
* |
* @param FFProbe |
* |
* @return FFMpeg |
*/ |
public function setFFProbe(FFProbe $ffprobe) |
{ |
$this->ffprobe = $ffprobe; |
|
return $this; |
} |
|
/** |
* Gets FFProbe. |
* |
* @return FFProbe |
*/ |
public function getFFProbe() |
{ |
return $this->ffprobe; |
} |
|
/** |
* Sets the ffmpeg driver. |
* |
* @return FFMpeg |
*/ |
public function setFFMpegDriver(FFMpegDriver $ffmpeg) |
{ |
$this->driver = $ffmpeg; |
|
return $this; |
} |
|
/** |
* Gets the ffmpeg driver. |
* |
* @return FFMpegDriver |
*/ |
public function getFFMpegDriver() |
{ |
return $this->driver; |
} |
|
/** |
* Opens a file in order to be processed. |
* |
* @param string $pathfile A pathfile |
* |
* @return Audio|Video |
* |
* @throws InvalidArgumentException |
*/ |
public function open($pathfile) |
{ |
if (null === $streams = $this->ffprobe->streams($pathfile)) { |
throw new RuntimeException(sprintf('Unable to probe "%s".', $pathfile)); |
} |
|
if (0 < count($streams->videos())) { |
return new Video($pathfile, $this->driver, $this->ffprobe); |
} elseif (0 < count($streams->audios())) { |
return new Audio($pathfile, $this->driver, $this->ffprobe); |
} |
|
throw new InvalidArgumentException('Unable to detect file format, only audio and video supported'); |
} |
|
/** |
* Creates a new FFMpeg instance. |
* |
* @param array|ConfigurationInterface $configuration |
* @param LoggerInterface $logger |
* @param FFProbe $probe |
* |
* @return FFMpeg |
*/ |
public static function create($configuration = array(), LoggerInterface $logger = null, FFProbe $probe = null) |
{ |
if (null === $probe) { |
$probe = FFProbe::create($configuration, $logger, null); |
} |
|
return new static(FFMpegDriver::create($logger, $configuration), $probe); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFMpegServiceProvider.php |
@@ -0,0 +1,64 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg; |
|
use Doctrine\Common\Cache\ArrayCache; |
use Silex\Application; |
use Silex\ServiceProviderInterface; |
|
class FFMpegServiceProvider implements ServiceProviderInterface |
{ |
public function register(Application $app) |
{ |
$app['ffmpeg.configuration'] = array(); |
$app['ffmpeg.default.configuration'] = array( |
'ffmpeg.threads' => 4, |
'ffmpeg.timeout' => 300, |
'ffmpeg.binaries' => array('avconv', 'ffmpeg'), |
'ffprobe.timeout' => 30, |
'ffprobe.binaries' => array('avprobe', 'ffprobe'), |
); |
$app['ffmpeg.logger'] = null; |
|
$app['ffmpeg.configuration.build'] = $app->share(function (Application $app) { |
return array_replace($app['ffmpeg.default.configuration'], $app['ffmpeg.configuration']); |
}); |
|
$app['ffmpeg'] = $app['ffmpeg.ffmpeg'] = $app->share(function (Application $app) { |
$configuration = $app['ffmpeg.configuration.build']; |
|
if (isset($configuration['ffmpeg.timeout'])) { |
$configuration['timeout'] = $configuration['ffmpeg.timeout']; |
} |
|
return FFMpeg::create($configuration, $app['ffmpeg.logger'], $app['ffmpeg.ffprobe']); |
}); |
|
$app['ffprobe.cache'] = $app->share(function () { |
return new ArrayCache(); |
}); |
|
$app['ffmpeg.ffprobe'] = $app->share(function (Application $app) { |
$configuration = $app['ffmpeg.configuration.build']; |
|
if (isset($configuration['ffmpeg.timeout'])) { |
$configuration['timeout'] = $configuration['ffprobe.timeout']; |
} |
|
return FFProbe::create($configuration, $app['ffmpeg.logger'], $app['ffprobe.cache']); |
}); |
} |
|
public function boot(Application $app) |
{ |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFProbe/DataMapping/AbstractData.php |
@@ -0,0 +1,93 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\FFProbe\DataMapping; |
|
abstract class AbstractData implements \Countable |
{ |
private $properties; |
|
public function __construct(array $properties) |
{ |
$this->properties = $properties; |
} |
|
/** |
* Returns true if data has property. |
* |
* @param string $property |
* @return Boolean |
*/ |
public function has($property) |
{ |
return isset($this->properties[$property]); |
} |
|
/** |
* Returns the property value given its name. |
* |
* @param string $property |
* @param mixed $default |
* |
* @return mixed |
*/ |
public function get($property, $default = null) |
{ |
if (!isset($this->properties[$property])) { |
return $default; |
} |
|
return $this->properties[$property]; |
} |
|
/** |
* Sets the property value given its name. |
* |
* @param string $property |
* @param mixed $value |
* |
* @return AbstractData |
*/ |
public function set($property, $value) |
{ |
$this->properties[$property] = $value; |
|
return $this; |
} |
|
/** |
* Returns all property names. |
* |
* @return array |
*/ |
public function keys() |
{ |
return array_keys($this->properties); |
} |
|
/** |
* Returns all properties and their values. |
* |
* @return array |
*/ |
public function all() |
{ |
return $this->properties; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function count() |
{ |
return count($this->properties); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFProbe/DataMapping/Stream.php |
@@ -0,0 +1,107 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\FFProbe\DataMapping; |
|
use FFMpeg\Exception\LogicException; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Coordinate\Dimension; |
|
class Stream extends AbstractData |
{ |
/** |
* Returns true if the stream is an audio stream. |
* |
* @return Boolean |
*/ |
public function isAudio() |
{ |
return $this->get('codec_type') === 'audio'; |
} |
|
/** |
* Returns true if the stream is a video stream. |
* |
* @return Boolean |
*/ |
public function isVideo() |
{ |
return $this->get('codec_type') === 'video'; |
} |
|
/** |
* Returns the dimension of the video stream. |
* |
* @return Dimension |
* |
* @throws LogicException In case the stream is not a video stream. |
* @throws RuntimeException In case the dimensions can not be extracted. |
*/ |
public function getDimensions() |
{ |
if (!$this->isVideo()) { |
throw new LogicException('Dimensions can only be retrieved from video streams.'); |
} |
|
$sampleRatio = $displayRatio = null; |
|
$width = $this->get('width'); |
$height = $this->get('height'); |
|
if (null !== $ratio = $this->extractRatio($this, 'sample_aspect_ratio')) { |
$sampleRatio = $ratio; |
} |
if (null !== $ratio = $this->extractRatio($this, 'display_aspect_ratio')) { |
$displayRatio = $ratio; |
} |
|
if (null === $height || null === $width) { |
throw new RuntimeException('Unable to extract dimensions.'); |
} |
|
if (null !== $displayRatio && null !== $sampleRatio) { |
if ($sampleRatio[0] !== 1 && $sampleRatio[1] !== 1) { |
if (null !== $width && null !== $height) { |
// stretch video according to pixel sample aspect ratio |
$width = round($width * ($sampleRatio[0] / $sampleRatio[1])); |
// set height according to display aspect ratio |
$height = round($width * ($displayRatio[1] / $displayRatio[0])); |
} |
} |
} |
|
return new Dimension($width, $height); |
} |
|
/** |
* Extracts a ratio from a string in a \d+:\d+ format given a key name. |
* |
* @param Stream $stream The stream where to look for the ratio. |
* @param string $name the name of the key. |
* @return null|array An array containing the width and the height, null if not found. |
*/ |
private function extractRatio(Stream $stream, $name) |
{ |
if (!$stream->has($name)) { |
return; |
} |
|
$ratio = $stream->get($name); |
if (preg_match('/\d+:\d+/', $ratio)) { |
$data = array_filter(explode(':', $ratio), function ($int) { |
return $int > 0; |
}); |
if (2 === count($data)) { |
return array_map(function ($int) { return (int) $int; }, $data); |
} |
} |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFProbe/DataMapping/StreamCollection.php |
@@ -0,0 +1,99 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\FFProbe\DataMapping; |
|
class StreamCollection implements \Countable, \IteratorAggregate |
{ |
private $streams; |
|
public function __construct(array $streams = array()) |
{ |
$this->streams = array_values($streams); |
} |
|
/** |
* Returns the first stream of the collection, null if the collection is |
* empty. |
* |
* @return null|Stream |
*/ |
public function first() |
{ |
$stream = reset($this->streams); |
|
return $stream ?: null; |
} |
|
/** |
* Adds a stream to the collection. |
* |
* @param Stream $stream |
* |
* @return StreamCollection |
*/ |
public function add(Stream $stream) |
{ |
$this->streams[] = $stream; |
|
return $this; |
} |
|
/** |
* Returns a new StreamCollection with only video streams. |
* |
* @return StreamCollection |
*/ |
public function videos() |
{ |
return new static(array_filter($this->streams, function (Stream $stream) { |
return $stream->isVideo(); |
})); |
} |
|
/** |
* Returns a new StreamCollection with only audio streams. |
* |
* @return StreamCollection |
*/ |
public function audios() |
{ |
return new static(array_filter($this->streams, function (Stream $stream) { |
return $stream->isAudio(); |
})); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function count() |
{ |
return count($this->streams); |
} |
|
/** |
* Returns the array of contained streams. |
* |
* @return array |
*/ |
public function all() |
{ |
return $this->streams; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getIterator() |
{ |
return new \ArrayIterator($this->streams); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFProbe/OptionsTester.php |
@@ -0,0 +1,70 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\FFProbe; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use Doctrine\Common\Cache\Cache; |
use FFMpeg\Driver\FFProbeDriver; |
use FFMpeg\Exception\RuntimeException; |
|
class OptionsTester implements OptionsTesterInterface |
{ |
/** @var FFProbeDriver */ |
private $ffprobe; |
/** @var Cache */ |
private $cache; |
|
public function __construct(FFProbeDriver $ffprobe, Cache $cache) |
{ |
$this->ffprobe = $ffprobe; |
$this->cache = $cache; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function has($name) |
{ |
$id = sprintf('option-%s', $name); |
|
if ($this->cache->contains($id)) { |
return $this->cache->fetch($id); |
} |
|
$output = $this->retrieveHelpOutput(); |
|
$ret = (Boolean) preg_match('/^'.$name.'/m', $output); |
|
$this->cache->save($id, $ret); |
|
return $ret; |
} |
|
private function retrieveHelpOutput() |
{ |
$id = 'help'; |
|
if ($this->cache->contains($id)) { |
return $this->cache->fetch($id); |
} |
|
try { |
$output = $this->ffprobe->command(array('-help', '-loglevel', 'quiet')); |
} catch (ExecutionFailureException $e) { |
throw new RuntimeException('Your FFProbe version is too old and does not support `-help` option, please upgrade.', $e->getCode(), $e); |
} |
|
$this->cache->save($id, $output); |
|
return $output; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFProbe/OutputParser.php |
@@ -0,0 +1,125 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\FFProbe; |
|
use FFMpeg\FFProbe; |
use FFMpeg\Exception\InvalidArgumentException; |
|
class OutputParser implements OutputParserInterface |
{ |
/** |
* {@inheritdoc} |
*/ |
public function parse($type, $data) |
{ |
switch ($type) { |
case FFProbe::TYPE_FORMAT: |
return $this->parseFormat($data); |
break; |
case FFProbe::TYPE_STREAMS: |
return $this->parseStreams($data); |
break; |
default: |
throw new InvalidArgumentException(sprintf('Unknown data type %s', $type)); |
} |
} |
|
private function parseFormat($data) |
{ |
$ret = array(); |
|
foreach (explode(PHP_EOL, $data) as $line) { |
|
if (in_array($line, array('[FORMAT]', '[/FORMAT]'))) { |
continue; |
} |
|
$chunks = explode('=', $line); |
$key = array_shift($chunks); |
|
if ('' === trim($key)) { |
continue; |
} |
|
$value = trim(implode('=', $chunks)); |
|
if ('nb_streams' === $key) { |
$value = (int) $value; |
} |
|
if (0 === strpos($key, 'TAG:')) { |
if (!isset($ret['tags'])) { |
$ret['tags'] = array(); |
} |
$ret['tags'][substr($key, 4)] = $value; |
} else { |
$ret[$key] = $value; |
} |
} |
|
return array('format' => $ret); |
} |
|
private function parseStreams($data) |
{ |
$ret = array(); |
$n = -1; |
|
foreach (explode(PHP_EOL, $data) as $line) { |
|
if ($line == '[STREAM]') { |
$n ++; |
$ret[$n] = array(); |
continue; |
} |
if ($line == '[/STREAM]') { |
continue; |
} |
|
$chunks = explode('=', $line); |
$key = array_shift($chunks); |
|
if ('' === trim($key)) { |
continue; |
} |
|
$value = trim(implode('=', $chunks)); |
|
if ('N/A' === $value) { |
continue; |
} |
if ('profile' === $key && 'unknown' === $value) { |
continue; |
} |
|
if (in_array($key, array('index', 'width', 'height', 'channels', 'bits_per_sample', 'has_b_frames', 'level', 'start_pts', 'duration_ts'))) { |
$value = (int) $value; |
} |
|
if (0 === strpos($key, 'TAG:')) { |
if (!isset($ret[$n]['tags'])) { |
$ret[$n]['tags'] = array(); |
} |
$ret[$n]['tags'][substr($key, 4)] = $value; |
} elseif (0 === strpos($key, 'DISPOSITION:')) { |
if (!isset($ret[$n]['disposition'])) { |
$ret[$n]['disposition'] = array(); |
} |
$ret[$n]['disposition'][substr($key, 12)] = $value; |
} else { |
$ret[$n][$key] = $value; |
} |
} |
|
return array('streams' => $ret); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/FFProbe.php |
@@ -0,0 +1,275 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg; |
|
use Alchemy\BinaryDriver\ConfigurationInterface; |
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use Doctrine\Common\Cache\ArrayCache; |
use Doctrine\Common\Cache\Cache; |
use FFMpeg\Driver\FFProbeDriver; |
use FFMpeg\FFProbe\DataMapping\Format; |
use FFMpeg\FFProbe\Mapper; |
use FFMpeg\FFProbe\MapperInterface; |
use FFMpeg\FFProbe\OptionsTester; |
use FFMpeg\FFProbe\OptionsTesterInterface; |
use FFMpeg\FFProbe\OutputParser; |
use FFMpeg\FFProbe\OutputParserInterface; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\FFProbe\DataMapping\StreamCollection; |
use Psr\Log\LoggerInterface; |
|
class FFProbe |
{ |
const TYPE_STREAMS = 'streams'; |
const TYPE_FORMAT = 'format'; |
|
/** @var Cache */ |
private $cache; |
/** @var OptionsTesterInterface */ |
private $optionsTester; |
/** @var OutputParserInterface */ |
private $parser; |
/** @var FFProbeDriver */ |
private $ffprobe; |
/** @var MapperInterface */ |
private $mapper; |
|
public function __construct(FFProbeDriver $ffprobe, Cache $cache) |
{ |
$this->ffprobe = $ffprobe; |
$this->optionsTester = new OptionsTester($ffprobe, $cache); |
$this->parser = new OutputParser(); |
$this->mapper = new Mapper(); |
$this->cache = $cache; |
} |
|
/** |
* @return OutputParserInterface |
*/ |
public function getParser() |
{ |
return $this->parser; |
} |
|
/** |
* @param OutputParserInterface $parser |
* |
* @return FFProbe |
*/ |
public function setParser(OutputParserInterface $parser) |
{ |
$this->parser = $parser; |
|
return $this; |
} |
|
/** |
* @return FFProbeDriver |
*/ |
public function getFFProbeDriver() |
{ |
return $this->ffprobe; |
} |
|
/** |
* @param FFProbeDriver $ffprobe |
* |
* @return FFProbe |
*/ |
public function setFFProbeDriver(FFProbeDriver $ffprobe) |
{ |
$this->ffprobe = $ffprobe; |
|
return $this; |
} |
|
/** |
* @param OptionsTesterInterface $tester |
* |
* @return FFProbe |
*/ |
public function setOptionsTester(OptionsTesterInterface $tester) |
{ |
$this->optionsTester = $tester; |
|
return $this; |
} |
|
/** |
* @return OptionsTesterInterface |
*/ |
public function getOptionsTester() |
{ |
return $this->optionsTester; |
} |
|
/** |
* @param Cache $cache |
* |
* @return FFProbe |
*/ |
public function setCache(Cache $cache) |
{ |
$this->cache = $cache; |
|
return $this; |
} |
|
/** |
* @return Cache |
*/ |
public function getCache() |
{ |
return $this->cache; |
} |
|
/** |
* @return MapperInterface |
*/ |
public function getMapper() |
{ |
return $this->mapper; |
} |
|
/** |
* @param MapperInterface $mapper |
* |
* @return FFProbe |
*/ |
public function setMapper(MapperInterface $mapper) |
{ |
$this->mapper = $mapper; |
|
return $this; |
} |
|
/** |
* @api |
* |
* Probes the format of a given file. |
* |
* @param string $pathfile |
* |
* @return Format A Format object |
* |
* @throws InvalidArgumentException |
* @throws RuntimeException |
*/ |
public function format($pathfile) |
{ |
return $this->probe($pathfile, '-show_format', static::TYPE_FORMAT); |
} |
|
/** |
* @api |
* |
* Probes the streams contained in a given file. |
* |
* @param string $pathfile |
* |
* @return StreamCollection A collection of streams |
* |
* @throws InvalidArgumentException |
* @throws RuntimeException |
*/ |
public function streams($pathfile) |
{ |
return $this->probe($pathfile, '-show_streams', static::TYPE_STREAMS); |
} |
|
/** |
* @api |
* |
* Creates an FFProbe. |
* |
* @param array|ConfigurationInterface $configuration |
* @param LoggerInterface $logger |
* @param Cache $cache |
* |
* @return FFProbe |
*/ |
public static function create($configuration = array(), LoggerInterface $logger = null, Cache $cache = null) |
{ |
if (null === $cache) { |
$cache = new ArrayCache(); |
} |
|
return new static(FFProbeDriver::create($configuration, $logger), $cache); |
} |
|
private function probe($pathfile, $command, $type, $allowJson = true) |
{ |
$id = sprintf('%s-%s', $command, $pathfile); |
|
if ($this->cache->contains($id)) { |
return $this->cache->fetch($id); |
} |
|
if (!$this->optionsTester->has($command)) { |
throw new RuntimeException(sprintf( |
'This version of ffprobe is too old and ' |
. 'does not support `%s` option, please upgrade', $command |
)); |
} |
|
$commands = array($pathfile, $command); |
|
$parseIsToDo = false; |
|
if ($allowJson && $this->optionsTester->has('-print_format')) { |
// allowed in latest PHP-FFmpeg version |
$commands[] = '-print_format'; |
$commands[] = 'json'; |
} elseif ($allowJson && $this->optionsTester->has('-of')) { |
// option has changed in avconv 9 |
$commands[] = '-of'; |
$commands[] = 'json'; |
} else { |
$parseIsToDo = true; |
} |
|
try { |
$output = $this->ffprobe->command($commands); |
} catch (ExecutionFailureException $e) { |
throw new RuntimeException(sprintf('Unable to probe %s', $pathfile), $e->getCode(), $e); |
} |
|
if ($parseIsToDo) { |
$data = $this->parser->parse($type, $output); |
} else { |
try { |
// Malformed json may be retrieved |
$data = $this->parseJson($output); |
} catch (RuntimeException $e) { |
return $this->probe($pathfile, $command, $type, false); |
} |
} |
|
$ret = $this->mapper->map($type, $data); |
|
$this->cache->save($id, $ret); |
|
return $ret; |
} |
|
private function parseJson($data) |
{ |
$ret = @json_decode($data, true); |
|
if (JSON_ERROR_NONE !== json_last_error()) { |
throw new RuntimeException(sprintf('Unable to parse json %s', $ret)); |
} |
|
return $ret; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Filters/Audio/AudioFilters.php |
@@ -0,0 +1,74 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Filters\Audio; |
|
use FFMpeg\Filters\Audio\AddMetadataFilter; |
use FFMpeg\Media\Audio; |
use FFMpeg\Coordinate\TimeCode; |
|
class AudioFilters |
{ |
protected $media; |
|
public function __construct(Audio $media) |
{ |
$this->media = $media; |
} |
|
/** |
* Resamples the audio file. |
* |
* @param Integer $rate |
* |
* @return AudioFilters |
*/ |
public function resample($rate) |
{ |
$this->media->addFilter(new AudioResamplableFilter($rate)); |
|
return $this; |
} |
|
/** |
* Add metadata to an audio file. If no arguments are given then filter |
* will remove all metadata from the audio file |
* @param Array|Null $data If array must contain one of these key/value pairs: |
* - "title": Title metadata |
* - "artist": Artist metadata |
* - "composer": Composer metadata |
* - "album": Album metadata |
* - "track": Track metadata |
* - "artwork": Song artwork. String of file path |
* - "year": Year metadata |
* - "genre": Genre metadata |
* - "description": Description metadata |
*/ |
public function addMetadata($data = null) |
{ |
$this->media->addFilter(new AddMetadataFilter($data)); |
|
return $this; |
} |
|
/** |
* Cuts the audio at `$start`, optionally define the end |
* |
* @param TimeCode $start Where the clipping starts(seek to time) |
* @param TimeCode $duration How long the clipped audio should be |
* @return AudioFilters |
*/ |
public function clip($start, $duration = null) { |
$this->media->addFilter(new AudioClipFilter($start, $duration)); |
|
return $this; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Filters/Video/ExtractMultipleFramesFilter.php |
@@ -0,0 +1,128 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Strime <romain@strime.io> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Filters\Video; |
|
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Media\Video; |
use FFMpeg\Format\VideoInterface; |
|
class ExtractMultipleFramesFilter implements VideoFilterInterface |
{ |
/** will extract a frame every second */ |
const FRAMERATE_EVERY_SEC = '1/1'; |
/** will extract a frame every 2 seconds */ |
const FRAMERATE_EVERY_2SEC = '1/2'; |
/** will extract a frame every 5 seconds */ |
const FRAMERATE_EVERY_5SEC = '1/5'; |
/** will extract a frame every 10 seconds */ |
const FRAMERATE_EVERY_10SEC = '1/10'; |
/** will extract a frame every 30 seconds */ |
const FRAMERATE_EVERY_30SEC = '1/30'; |
/** will extract a frame every minute */ |
const FRAMERATE_EVERY_60SEC = '1/60'; |
|
/** @var integer */ |
private $priority; |
private $frameRate; |
private $destinationFolder; |
|
public function __construct($frameRate = self::FRAMERATE_EVERY_SEC, $destinationFolder = __DIR__, $priority = 0) |
{ |
$this->priority = $priority; |
$this->frameRate = $frameRate; |
|
// Make sure that the destination folder has a trailing slash |
if(strcmp( substr($destinationFolder, -1), "/") != 0) |
$destinationFolder .= "/"; |
|
// Set the destination folder |
$this->destinationFolder = $destinationFolder; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getPriority() |
{ |
return $this->priority; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getFrameRate() |
{ |
return $this->frameRate; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getDestinationFolder() |
{ |
return $this->destinationFolder; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function apply(Video $video, VideoInterface $format) |
{ |
$commands = array(); |
$duration = 0; |
|
try { |
// Get the duration of the video |
foreach ($video->getStreams()->videos() as $stream) { |
if ($stream->has('duration')) { |
$duration = $stream->get('duration'); |
} |
} |
|
// Get the number of frames per second we have to extract. |
if(preg_match('/(\d+)(?:\s*)([\+\-\*\/])(?:\s*)(\d+)/', $this->frameRate, $matches) !== FALSE){ |
$operator = $matches[2]; |
|
switch($operator){ |
case '/': |
$nbFramesPerSecond = $matches[1] / $matches[3]; |
break; |
|
default: |
throw new InvalidArgumentException('The frame rate is not a proper division: ' . $this->frameRate); |
break; |
} |
} |
|
// Set the number of digits to use in the exported filenames |
$nbImages = ceil( $duration * $nbFramesPerSecond ); |
|
if($nbImages < 100) |
$nbDigitsInFileNames = "02"; |
elseif($nbImages < 1000) |
$nbDigitsInFileNames = "03"; |
else |
$nbDigitsInFileNames = "06"; |
|
// Set the parameters |
$commands[] = '-vf'; |
$commands[] = 'fps=' . $this->frameRate; |
$commands[] = $this->destinationFolder . 'frame-%'.$nbDigitsInFileNames.'d.jpg'; |
} |
catch (RuntimeException $e) { |
throw new RuntimeException('An error occured while extracting the frames: ' . $e->getMessage() . '. The code: ' . $e->getCode()); |
} |
|
return $commands; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Filters/Video/ResizeFilter.php |
@@ -0,0 +1,143 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <dev.team@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Filters\Video; |
|
use FFMpeg\Coordinate\Dimension; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Media\Video; |
use FFMpeg\Format\VideoInterface; |
|
class ResizeFilter implements VideoFilterInterface |
{ |
/** fits to the dimensions, might introduce anamorphosis */ |
const RESIZEMODE_FIT = 'fit'; |
/** resizes the video inside the given dimension, no anamorphosis */ |
const RESIZEMODE_INSET = 'inset'; |
/** resizes the video to fit the dimension width, no anamorphosis */ |
const RESIZEMODE_SCALE_WIDTH = 'width'; |
/** resizes the video to fit the dimension height, no anamorphosis */ |
const RESIZEMODE_SCALE_HEIGHT = 'height'; |
|
/** @var Dimension */ |
private $dimension; |
/** @var string */ |
private $mode; |
/** @var Boolean */ |
private $forceStandards; |
/** @var integer */ |
private $priority; |
|
public function __construct(Dimension $dimension, $mode = self::RESIZEMODE_FIT, $forceStandards = true, $priority = 0) |
{ |
$this->dimension = $dimension; |
$this->mode = $mode; |
$this->forceStandards = $forceStandards; |
$this->priority = $priority; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getPriority() |
{ |
return $this->priority; |
} |
|
/** |
* @return Dimension |
*/ |
public function getDimension() |
{ |
return $this->dimension; |
} |
|
/** |
* @return string |
*/ |
public function getMode() |
{ |
return $this->mode; |
} |
|
/** |
* @return Boolean |
*/ |
public function areStandardsForced() |
{ |
return $this->forceStandards; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function apply(Video $video, VideoInterface $format) |
{ |
$dimensions = null; |
$commands = array(); |
|
foreach ($video->getStreams() as $stream) { |
if ($stream->isVideo()) { |
try { |
$dimensions = $stream->getDimensions(); |
break; |
} catch (RuntimeException $e) { |
|
} |
} |
} |
|
if (null !== $dimensions) { |
$dimensions = $this->getComputedDimensions($dimensions, $format->getModulus()); |
|
// Using Filter to have ordering |
$commands[] = '-vf'; |
$commands[] = '[in]scale=' . $dimensions->getWidth() . ':' . $dimensions->getHeight() . ' [out]'; |
|
} |
|
return $commands; |
} |
|
private function getComputedDimensions(Dimension $dimension, $modulus) |
{ |
$originalRatio = $dimension->getRatio($this->forceStandards); |
|
switch ($this->mode) { |
case self::RESIZEMODE_SCALE_WIDTH: |
$height = $this->dimension->getHeight(); |
$width = $originalRatio->calculateWidth($height, $modulus); |
break; |
case self::RESIZEMODE_SCALE_HEIGHT: |
$width = $this->dimension->getWidth(); |
$height = $originalRatio->calculateHeight($width, $modulus); |
break; |
case self::RESIZEMODE_INSET: |
$targetRatio = $this->dimension->getRatio($this->forceStandards); |
|
if ($targetRatio->getValue() > $originalRatio->getValue()) { |
$height = $this->dimension->getHeight(); |
$width = $originalRatio->calculateWidth($height, $modulus); |
} else { |
$width = $this->dimension->getWidth(); |
$height = $originalRatio->calculateHeight($width, $modulus); |
} |
break; |
case self::RESIZEMODE_FIT: |
default: |
$width = $this->dimension->getWidth(); |
$height = $this->dimension->getHeight(); |
break; |
} |
|
return new Dimension($width, $height); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Filters/Video/RotateFilter.php |
@@ -0,0 +1,82 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <dev.team@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Filters\Video; |
|
use FFMpeg\Coordinate\Dimension; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Media\Video; |
use FFMpeg\Format\VideoInterface; |
|
class RotateFilter implements VideoFilterInterface |
{ |
const ROTATE_90 = 'transpose=1'; |
const ROTATE_180 = 'hflip,vflip'; |
const ROTATE_270 = 'transpose=2'; |
|
/** @var string */ |
private $angle; |
/** @var integer */ |
private $priority; |
|
public function __construct($angle, $priority = 0) |
{ |
$this->setAngle($angle); |
$this->priority = (int) $priority; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getPriority() |
{ |
return $this->priority; |
} |
|
/** |
* @return Dimension |
*/ |
public function getAngle() |
{ |
return $this->angle; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function apply(Video $video, VideoInterface $format) |
{ |
if (in_array($this->angle, array(self::ROTATE_90, self::ROTATE_270), true)) { |
foreach ($video->getStreams()->videos() as $stream) { |
if ($stream->has('width') && $stream->has('height')) { |
$width = $stream->get('width'); |
$stream->set('width', $stream->get('height')); |
$stream->set('height', $width); |
} |
} |
} |
|
return array('-vf', $this->angle, '-metadata:s:v:0', 'rotate=0'); |
} |
|
private function setAngle($angle) |
{ |
switch ($angle) { |
case self::ROTATE_90: |
case self::ROTATE_180: |
case self::ROTATE_270: |
$this->angle = $angle; |
break; |
default: |
throw new InvalidArgumentException('Invalid angle value.'); |
} |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Filters/Video/VideoFilters.php |
@@ -0,0 +1,178 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <dev.team@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Filters\Video; |
|
use FFMpeg\Coordinate\Point; |
use FFMpeg\Media\Video; |
use FFMpeg\Coordinate\TimeCode; |
use FFMpeg\Coordinate\Dimension; |
use FFMpeg\Coordinate\FrameRate; |
use FFMpeg\Filters\Audio\AudioResamplableFilter; |
use FFMpeg\Filters\Audio\AudioFilters; |
|
class VideoFilters extends AudioFilters |
{ |
public function __construct(Video $media) |
{ |
parent::__construct($media); |
} |
|
/** |
* Resizes a video to a given dimension. |
* |
* @param Dimension $dimension |
* @param string $mode |
* @param Boolean $forceStandards |
* |
* @return VideoFilters |
*/ |
public function resize(Dimension $dimension, $mode = ResizeFilter::RESIZEMODE_FIT, $forceStandards = true) |
{ |
$this->media->addFilter(new ResizeFilter($dimension, $mode, $forceStandards)); |
|
return $this; |
} |
|
/** |
* Changes the video framerate. |
* |
* @param FrameRate $framerate |
* @param Integer $gop |
* |
* @return VideoFilters |
*/ |
public function framerate(FrameRate $framerate, $gop) |
{ |
$this->media->addFilter(new FrameRateFilter($framerate, $gop)); |
|
return $this; |
} |
|
/** |
* Extract multiple frames from the video |
* |
* @param string $frameRate |
* @param string $destinationFolder |
* |
* @return $this |
*/ |
public function extractMultipleFrames($frameRate = ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, $destinationFolder = __DIR__) |
{ |
$this->media->addFilter(new ExtractMultipleFramesFilter($frameRate, $destinationFolder)); |
|
return $this; |
} |
|
/** |
* Synchronizes audio and video. |
* |
* @return VideoFilters |
*/ |
public function synchronize() |
{ |
$this->media->addFilter(new SynchronizeFilter()); |
|
return $this; |
} |
|
/** |
* Clips (cuts) the video. |
* |
* @param TimeCode $start |
* @param TimeCode $duration |
* |
* @return VideoFilters |
*/ |
public function clip($start, $duration = null) |
{ |
$this->media->addFilter(new ClipFilter($start, $duration)); |
|
return $this; |
} |
|
/** |
* Resamples the audio file. |
* |
* @param Integer $rate |
* |
* @return AudioFilters |
*/ |
public function audioResample($rate) |
{ |
$this->media->addFilter(new AudioResamplableFilter($rate)); |
|
return $this; |
} |
|
/** |
* Adds padding (black bars) to a video. |
* |
* @param Dimension $dimension |
* |
* @return VideoFilters |
*/ |
public function pad(Dimension $dimension) |
{ |
$this->media->addFilter(new PadFilter($dimension)); |
|
return $this; |
} |
|
public function rotate($angle) |
{ |
$this->media->addFilter(new RotateFilter($angle, 30)); |
|
return $this; |
} |
|
/** |
* Crops the video |
* |
* @param Point $point |
* @param Dimension $dimension |
* |
* @return VideoFilters |
*/ |
public function crop(Point $point, Dimension $dimension) |
{ |
$this->media->addFilter(new CropFilter($point, $dimension)); |
|
return $this; |
} |
|
/** |
* @param string $imagePath |
* @param array $coordinates |
* |
* @return $this |
*/ |
public function watermark($imagePath, array $coordinates = array()) |
{ |
$this->media->addFilter(new WatermarkFilter($imagePath, $coordinates)); |
|
return $this; |
} |
|
/** |
* Applies a custom filter: -vf foo bar |
* |
* @param string $parameters |
* |
* @return VideoFilters |
*/ |
public function custom($parameters) |
{ |
$this->media->addFilter(new CustomFilter($parameters)); |
|
return $this; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Filters/Video/WatermarkFilter.php |
@@ -0,0 +1,80 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <dev.team@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Filters\Video; |
|
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Format\VideoInterface; |
use FFMpeg\Media\Video; |
|
class WatermarkFilter implements VideoFilterInterface |
{ |
/** @var string */ |
private $watermarkPath; |
/** @var array */ |
private $coordinates; |
/** @var integer */ |
private $priority; |
|
public function __construct($watermarkPath, array $coordinates = array(), $priority = 0) |
{ |
if (!file_exists($watermarkPath)) { |
throw new InvalidArgumentException(sprintf('File %s does not exist', $watermarkPath)); |
} |
|
$this->watermarkPath = $watermarkPath; |
$this->coordinates = $coordinates; |
$this->priority = $priority; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getPriority() |
{ |
return $this->priority; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function apply(Video $video, VideoInterface $format) |
{ |
$position = isset($this->coordinates['position']) ? $this->coordinates['position'] : 'absolute'; |
|
switch ($position) { |
case 'relative': |
if (isset($this->coordinates['top'])) { |
$y = $this->coordinates['top']; |
} elseif (isset($this->coordinates['bottom'])) { |
$y = sprintf('main_h - %d - overlay_h', $this->coordinates['bottom']); |
} else { |
$y = 0; |
} |
|
if (isset($this->coordinates['left'])) { |
$x = $this->coordinates['left']; |
} elseif (isset($this->coordinates['right'])) { |
$x = sprintf('main_w - %d - overlay_w', $this->coordinates['right']); |
} else { |
$x = 0; |
} |
|
break; |
default: |
$x = isset($this->coordinates['x']) ? $this->coordinates['x'] : 0; |
$y = isset($this->coordinates['y']) ? $this->coordinates['y'] : 0; |
break; |
} |
|
return array('-vf', sprintf('movie=%s [watermark]; [in][watermark] overlay=%s:%s [out]', $this->watermarkPath, $x, $y)); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Format/Audio/DefaultAudio.php |
@@ -0,0 +1,142 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Format\Audio; |
|
use Evenement\EventEmitter; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Format\AudioInterface; |
use FFMpeg\Media\MediaTypeInterface; |
use FFMpeg\Format\ProgressableInterface; |
use FFMpeg\Format\ProgressListener\AudioProgressListener; |
use FFMpeg\FFProbe; |
|
abstract class DefaultAudio extends EventEmitter implements AudioInterface, ProgressableInterface |
{ |
/** @var string */ |
protected $audioCodec; |
|
/** @var integer */ |
protected $audioKiloBitrate = 128; |
|
/** @var integer */ |
protected $audioChannels = null; |
|
/** |
* {@inheritdoc} |
*/ |
public function getExtraParams() |
{ |
return array(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getAudioCodec() |
{ |
return $this->audioCodec; |
} |
|
/** |
* Sets the audio codec, Should be in the available ones, otherwise an |
* exception is thrown. |
* |
* @param string $audioCodec |
* |
* @throws InvalidArgumentException |
*/ |
public function setAudioCodec($audioCodec) |
{ |
if ( ! in_array($audioCodec, $this->getAvailableAudioCodecs())) { |
throw new InvalidArgumentException(sprintf( |
'Wrong audiocodec value for %s, available formats are %s' |
, $audioCodec, implode(', ', $this->getAvailableAudioCodecs()) |
)); |
} |
|
$this->audioCodec = $audioCodec; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getAudioKiloBitrate() |
{ |
return $this->audioKiloBitrate; |
} |
|
/** |
* Sets the kiloBitrate value. |
* |
* @param integer $kiloBitrate |
* @throws InvalidArgumentException |
*/ |
public function setAudioKiloBitrate($kiloBitrate) |
{ |
if ($kiloBitrate < 1) { |
throw new InvalidArgumentException('Wrong kiloBitrate value'); |
} |
|
$this->audioKiloBitrate = (int) $kiloBitrate; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getAudioChannels() |
{ |
return $this->audioChannels; |
} |
|
/** |
* Sets the channels value. |
* |
* @param integer $channels |
* @throws InvalidArgumentException |
*/ |
public function setAudioChannels($channels) |
{ |
if ($channels < 1) { |
throw new InvalidArgumentException('Wrong channels value'); |
} |
|
$this->audioChannels = (int) $channels; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total) |
{ |
$format = $this; |
$listener = new AudioProgressListener($ffprobe, $media->getPathfile(), $pass, $total); |
$listener->on('progress', function () use ($media, $format) { |
$format->emit('progress', array_merge(array($media, $format), func_get_args())); |
}); |
|
return array($listener); |
} |
|
/** |
* {@inheritDoc} |
*/ |
public function getPasses() |
{ |
return 1; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php |
@@ -0,0 +1,262 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Format\ProgressListener; |
|
use Alchemy\BinaryDriver\Listeners\ListenerInterface; |
use Evenement\EventEmitter; |
use FFMpeg\FFProbe; |
use FFMpeg\Exception\RuntimeException; |
|
/** |
* @author Robert Gruendler <r.gruendler@gmail.com> |
*/ |
abstract class AbstractProgressListener extends EventEmitter implements ListenerInterface |
{ |
/** @var integer */ |
private $duration; |
|
/** @var integer */ |
private $totalSize; |
|
/** @var integer */ |
private $currentSize; |
|
/** @var integer */ |
private $currentTime; |
|
/** @var double */ |
private $lastOutput = null; |
|
/** @var FFProbe */ |
private $ffprobe; |
|
/** @var string */ |
private $pathfile; |
|
/** @var Boolean */ |
private $initialized = false; |
|
/** @var integer */ |
private $currentPass; |
|
/** @var integer */ |
private $totalPass; |
|
/** |
* Transcoding rate in kb/s |
* |
* @var integer |
*/ |
private $rate; |
|
/** |
* Percentage of transcoding progress (0 - 100) |
* |
* @var integer |
*/ |
private $percent = 0; |
|
/** |
* Time remaining (seconds) |
* |
* @var integer |
*/ |
private $remaining = null; |
|
/** |
* @param FFProbe $ffprobe |
* @param string $pathfile |
* @param integer $currentPass The cureent pass number |
* @param integer $totalPass The total number of passes |
* |
* @throws RuntimeException |
*/ |
public function __construct(FFProbe $ffprobe, $pathfile, $currentPass, $totalPass) |
{ |
$this->ffprobe = $ffprobe; |
$this->pathfile = $pathfile; |
$this->currentPass = $currentPass; |
$this->totalPass = $totalPass; |
} |
|
/** |
* @return FFProbe |
*/ |
public function getFFProbe() |
{ |
return $this->ffprobe; |
} |
|
/** |
* @return string |
*/ |
public function getPathfile() |
{ |
return $this->pathfile; |
} |
|
/** |
* @return integer |
*/ |
public function getCurrentPass() |
{ |
return $this->currentPass; |
} |
|
/** |
* @return integer |
*/ |
public function getTotalPass() |
{ |
return $this->totalPass; |
} |
|
/** |
* @return int |
*/ |
public function getCurrentTime() |
{ |
return $this->currentTime; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle($type, $data) |
{ |
if (null !== $progress = $this->parseProgress($data)) { |
$this->emit('progress', array_values($progress)); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function forwardedEvents() |
{ |
return array(); |
} |
|
/** |
* Get the regex pattern to match a ffmpeg stderr status line |
*/ |
abstract protected function getPattern(); |
|
/** |
* @param string $progress A ffmpeg stderr progress output |
* |
* @return array the progressinfo array or null if there's no progress available yet. |
*/ |
private function parseProgress($progress) |
{ |
if (!$this->initialized) { |
$this->initialize(); |
} |
|
if (null === $this->totalSize || null === $this->duration) { |
return; |
} |
|
$matches = array(); |
|
if (preg_match($this->getPattern(), $progress, $matches) !== 1) { |
return null; |
} |
|
$currentDuration = $this->convertDuration($matches[2]); |
$currentTime = microtime(true); |
$currentSize = trim(str_replace('kb', '', strtolower(($matches[1])))); |
$percent = max(0, min(1, $currentDuration / $this->duration)); |
|
if ($this->lastOutput !== null) { |
$delta = $currentTime - $this->lastOutput; |
|
// Check the type of the currentSize variable and convert it to an integer if needed. |
if(!is_numeric($currentSize)) { |
$currentSize = (int)$currentSize; |
} |
|
$deltaSize = $currentSize - $this->currentSize; |
$rate = $deltaSize * $delta; |
if ($rate > 0) { |
$totalDuration = $this->totalSize / $rate; |
$this->remaining = floor($totalDuration - ($totalDuration * $percent)); |
$this->rate = floor($rate); |
} else { |
$this->remaining = 0; |
$this->rate = 0; |
} |
} |
|
$percent = $percent / $this->totalPass + ($this->currentPass - 1) / $this->totalPass; |
|
$this->percent = floor($percent * 100); |
$this->lastOutput = $currentTime; |
$this->currentSize = (int) $currentSize; |
$this->currentTime = $currentDuration; |
|
return $this->getProgressInfo(); |
} |
|
/** |
* |
* @param string $rawDuration in the format 00:00:00.00 |
* @return number |
*/ |
private function convertDuration($rawDuration) |
{ |
$ar = array_reverse(explode(":", $rawDuration)); |
$duration = floatval($ar[0]); |
if (!empty($ar[1])) { |
$duration += intval($ar[1]) * 60; |
} |
if (!empty($ar[2])) { |
$duration += intval($ar[2]) * 60 * 60; |
} |
|
return $duration; |
} |
|
/** |
* @return array |
*/ |
private function getProgressInfo() |
{ |
if ($this->remaining === null) { |
return null; |
} |
|
return array( |
'percent' => $this->percent, |
'remaining' => $this->remaining, |
'rate' => $this->rate |
); |
} |
|
private function initialize() |
{ |
try { |
$format = $this->ffprobe->format($this->pathfile); |
} catch (RuntimeException $e) { |
return; |
} |
|
if (false === $format->has('size') || false === $format->has('duration')) { |
return; |
} |
|
$this->totalSize = $format->get('size') / 1024; |
$this->duration = $format->get('duration'); |
|
$this->initialized = true; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Format/Video/DefaultVideo.php |
@@ -0,0 +1,141 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Format\Video; |
|
use FFMpeg\FFProbe; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Format\Audio\DefaultAudio; |
use FFMpeg\Format\VideoInterface; |
use FFMpeg\Media\MediaTypeInterface; |
use FFMpeg\Format\ProgressListener\VideoProgressListener; |
|
/** |
* The abstract default Video format |
*/ |
abstract class DefaultVideo extends DefaultAudio implements VideoInterface |
{ |
/** @var string */ |
protected $videoCodec; |
|
/** @var Integer */ |
protected $kiloBitrate = 1000; |
|
/** @var Integer */ |
protected $modulus = 16; |
|
/** @var Array */ |
protected $additionalParamaters; |
|
/** |
* {@inheritdoc} |
*/ |
public function getKiloBitrate() |
{ |
return $this->kiloBitrate; |
} |
|
/** |
* Sets the kiloBitrate value. |
* |
* @param integer $kiloBitrate |
* @throws InvalidArgumentException |
*/ |
public function setKiloBitrate($kiloBitrate) |
{ |
if ($kiloBitrate < 1) { |
throw new InvalidArgumentException('Wrong kiloBitrate value'); |
} |
|
$this->kiloBitrate = (int) $kiloBitrate; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getVideoCodec() |
{ |
return $this->videoCodec; |
} |
|
/** |
* Sets the video codec, Should be in the available ones, otherwise an |
* exception is thrown. |
* |
* @param string $videoCodec |
* @throws InvalidArgumentException |
*/ |
public function setVideoCodec($videoCodec) |
{ |
if ( ! in_array($videoCodec, $this->getAvailableVideoCodecs())) { |
throw new InvalidArgumentException(sprintf( |
'Wrong videocodec value for %s, available formats are %s' |
, $videoCodec, implode(', ', $this->getAvailableVideoCodecs()) |
)); |
} |
|
$this->videoCodec = $videoCodec; |
|
return $this; |
} |
|
/** |
* @return integer |
*/ |
public function getModulus() |
{ |
return $this->modulus; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getAdditionalParameters() |
{ |
return $this->additionalParamaters; |
} |
|
/** |
* Sets additional parameters. |
* |
* @param array $additionalParamaters |
* @throws InvalidArgumentException |
*/ |
public function setAdditionalParameters($additionalParamaters) |
{ |
if (!is_array($additionalParamaters)) { |
throw new InvalidArgumentException('Wrong additionalParamaters value'); |
} |
|
$this->additionalParamaters = $additionalParamaters; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total) |
{ |
$format = $this; |
$listeners = array(new VideoProgressListener($ffprobe, $media->getPathfile(), $pass, $total)); |
|
foreach ($listeners as $listener) { |
$listener->on('progress', function () use ($format, $media) { |
$format->emit('progress', array_merge(array($media, $format), func_get_args())); |
}); |
} |
|
return $listeners; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/AbstractMediaType.php |
@@ -0,0 +1,113 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <dev.team@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use FFMpeg\Driver\FFMpegDriver; |
use FFMpeg\FFProbe; |
use FFMpeg\Filters\FiltersCollection; |
|
abstract class AbstractMediaType implements MediaTypeInterface |
{ |
/** @var string */ |
protected $pathfile; |
/** @var FFMpegDriver */ |
protected $driver; |
/** @var FFProbe */ |
protected $ffprobe; |
/** @var FiltersCollection */ |
protected $filters; |
|
public function __construct($pathfile, FFMpegDriver $driver, FFProbe $ffprobe) |
{ |
$this->pathfile = $pathfile; |
$this->driver = $driver; |
$this->ffprobe = $ffprobe; |
$this->filters = new FiltersCollection(); |
} |
|
/** |
* @return FFMpegDriver |
*/ |
public function getFFMpegDriver() |
{ |
return $this->driver; |
} |
|
/** |
* @param FFMpegDriver $driver |
* |
* @return MediaTypeInterface |
*/ |
public function setFFMpegDriver(FFMpegDriver $driver) |
{ |
$this->driver = $driver; |
|
return $this; |
} |
|
/** |
* @return FFProbe |
*/ |
public function getFFProbe() |
{ |
return $this->ffprobe; |
} |
|
/** |
* @param FFProbe $ffprobe |
* |
* @return MediaTypeInterface |
*/ |
public function setFFProbe(FFProbe $ffprobe) |
{ |
$this->ffprobe = $ffprobe; |
|
return $this; |
} |
|
/** |
* @return string |
*/ |
public function getPathfile() |
{ |
return $this->pathfile; |
} |
|
/** |
* @param FiltersCollection $filters |
* |
* @return MediaTypeInterface |
*/ |
public function setFiltersCollection(FiltersCollection $filters) |
{ |
$this->filters = $filters; |
|
return $this; |
} |
|
/** |
* @return MediaTypeInterface |
*/ |
public function getFiltersCollection() |
{ |
return $this->filters; |
} |
|
protected function cleanupTemporaryFile($filename) |
{ |
if (file_exists($filename) && is_writable($filename)) { |
unlink($filename); |
} |
|
return $this; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/Audio.php |
@@ -0,0 +1,117 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use FFMpeg\Filters\Audio\AudioFilters; |
use FFMpeg\Format\FormatInterface; |
use FFMpeg\Filters\Audio\SimpleFilter; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Filters\Audio\AudioFilterInterface; |
use FFMpeg\Filters\FilterInterface; |
use FFMpeg\Format\ProgressableInterface; |
|
class Audio extends AbstractStreamableMedia |
{ |
/** |
* {@inheritdoc} |
* |
* @return AudioFilters |
*/ |
public function filters() |
{ |
return new AudioFilters($this); |
} |
|
/** |
* {@inheritdoc} |
* |
* @return Audio |
*/ |
public function addFilter(FilterInterface $filter) |
{ |
if (!$filter instanceof AudioFilterInterface) { |
throw new InvalidArgumentException('Audio only accepts AudioFilterInterface filters'); |
} |
|
$this->filters->add($filter); |
|
return $this; |
} |
|
/** |
* Exports the audio in the desired format, applies registered filters. |
* |
* @param FormatInterface $format |
* @param string $outputPathfile |
* |
* @return Audio |
* |
* @throws RuntimeException |
*/ |
public function save(FormatInterface $format, $outputPathfile) |
{ |
$listeners = null; |
|
if ($format instanceof ProgressableInterface) { |
$listeners = $format->createProgressListener($this, $this->ffprobe, 1, 1); |
} |
|
$commands = array('-y', '-i', $this->pathfile); |
|
$filters = clone $this->filters; |
$filters->add(new SimpleFilter($format->getExtraParams(), 10)); |
|
if ($this->driver->getConfiguration()->has('ffmpeg.threads')) { |
$filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')))); |
} |
if (null !== $format->getAudioCodec()) { |
$filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec()))); |
} |
|
foreach ($filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this, $format)); |
} |
|
if (null !== $format->getAudioKiloBitrate()) { |
$commands[] = '-b:a'; |
$commands[] = $format->getAudioKiloBitrate() . 'k'; |
} |
if (null !== $format->getAudioChannels()) { |
$commands[] = '-ac'; |
$commands[] = $format->getAudioChannels(); |
} |
$commands[] = $outputPathfile; |
|
try { |
$this->driver->command($commands, false, $listeners); |
} catch (ExecutionFailureException $e) { |
$this->cleanupTemporaryFile($outputPathfile); |
throw new RuntimeException('Encoding failed', $e->getCode(), $e); |
} |
|
return $this; |
} |
|
/** |
* Gets the waveform of the video. |
* |
* @param integer $width |
* @param integer $height |
* @return Waveform |
*/ |
public function waveform($width = 640, $height = 120) |
{ |
return new Waveform($this, $this->driver, $this->ffprobe, $width, $height); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/Concat.php |
@@ -0,0 +1,264 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Strime <contact@strime.io> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use Alchemy\BinaryDriver\Exception\InvalidArgumentException; |
use FFMpeg\Filters\Concat\ConcatFilterInterface; |
use FFMpeg\Filters\Concat\ConcatFilters; |
use FFMpeg\Driver\FFMpegDriver; |
use FFMpeg\FFProbe; |
use FFMpeg\Filters\Audio\SimpleFilter; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Format\FormatInterface; |
use FFMpeg\Filters\FilterInterface; |
use FFMpeg\Format\ProgressableInterface; |
use FFMpeg\Format\AudioInterface; |
use FFMpeg\Format\VideoInterface; |
use Neutron\TemporaryFilesystem\Manager as FsManager; |
|
class Concat extends AbstractMediaType |
{ |
/** @var array */ |
private $sources; |
|
public function __construct($sources, FFMpegDriver $driver, FFProbe $ffprobe) |
{ |
parent::__construct($sources, $driver, $ffprobe); |
$this->sources = $sources; |
} |
|
/** |
* Returns the path to the sources. |
* |
* @return string |
*/ |
public function getSources() |
{ |
return $this->sources; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return ConcatFilters |
*/ |
public function filters() |
{ |
return new ConcatFilters($this); |
} |
|
/** |
* {@inheritdoc} |
* |
* @return Concat |
*/ |
public function addFilter(ConcatFilterInterface $filter) |
{ |
$this->filters->add($filter); |
|
return $this; |
} |
|
/** |
* Saves the concatenated video in the given array, considering that the sources videos are all encoded with the same codec. |
* |
* @param array $outputPathfile |
* @param string $streamCopy |
* |
* @return Concat |
* |
* @throws RuntimeException |
*/ |
public function saveFromSameCodecs($outputPathfile, $streamCopy = TRUE) |
{ |
/** |
* @see https://ffmpeg.org/ffmpeg-formats.html#concat |
* @see https://trac.ffmpeg.org/wiki/Concatenate |
*/ |
|
// Create the file which will contain the list of videos |
$fs = FsManager::create(); |
$sourcesFile = $fs->createTemporaryFile('ffmpeg-concat'); |
|
// Set the content of this file |
$fileStream = @fopen($sourcesFile, 'w'); |
|
if($fileStream === false) { |
throw new ExecutionFailureException('Cannot open the temporary file.'); |
} |
|
$count_videos = 0; |
if(is_array($this->sources) && (count($this->sources) > 0)) { |
foreach ($this->sources as $videoPath) { |
$line = ""; |
|
if($count_videos != 0) |
$line .= "\n"; |
|
$line .= "file ".$videoPath; |
|
fwrite($fileStream, $line); |
|
$count_videos++; |
} |
} |
else { |
throw new InvalidArgumentException('The list of videos is not a valid array.'); |
} |
fclose($fileStream); |
|
|
$commands = array( |
'-f', 'concat', '-safe', '0', |
'-i', $sourcesFile |
); |
|
// Check if stream copy is activated |
if($streamCopy === TRUE) { |
$commands[] = '-c'; |
$commands[] = 'copy'; |
} |
|
// If not, we can apply filters |
else { |
foreach ($this->filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this)); |
} |
} |
|
// Set the output file in the command |
$commands = array_merge($commands, array($outputPathfile)); |
|
// Execute the command |
try { |
$this->driver->command($commands); |
} catch (ExecutionFailureException $e) { |
$this->cleanupTemporaryFile($outputPathfile); |
$this->cleanupTemporaryFile($sourcesFile); |
throw new RuntimeException('Unable to save concatenated video', $e->getCode(), $e); |
} |
|
$this->cleanupTemporaryFile($sourcesFile); |
return $this; |
} |
|
/** |
* Saves the concatenated video in the given filename, considering that the sources videos are all encoded with the same codec. |
* |
* @param string $outputPathfile |
* |
* @return Concat |
* |
* @throws RuntimeException |
*/ |
public function saveFromDifferentCodecs(FormatInterface $format, $outputPathfile) |
{ |
/** |
* @see https://ffmpeg.org/ffmpeg-formats.html#concat |
* @see https://trac.ffmpeg.org/wiki/Concatenate |
*/ |
|
// Check the validity of the parameter |
if(!is_array($this->sources) || (count($this->sources) == 0)) { |
throw new InvalidArgumentException('The list of videos is not a valid array.'); |
} |
|
// Create the commands variable |
$commands = array(); |
|
// Prepare the parameters |
$nbSources = 0; |
$files = array(); |
|
// For each source, check if this is a legit file |
// and prepare the parameters |
foreach ($this->sources as $videoPath) { |
$files[] = '-i'; |
$files[] = $videoPath; |
$nbSources++; |
} |
|
$commands = array_merge($commands, $files); |
|
// Set the parameters of the request |
$commands[] = '-filter_complex'; |
|
$complex_filter = ''; |
for($i=0; $i<$nbSources; $i++) { |
$complex_filter .= '['.$i.':v:0] ['.$i.':a:0] '; |
} |
$complex_filter .= 'concat=n='.$nbSources.':v=1:a=1 [v] [a]'; |
|
$commands[] = $complex_filter; |
$commands[] = '-map'; |
$commands[] = '[v]'; |
$commands[] = '-map'; |
$commands[] = '[a]'; |
|
// Prepare the filters |
$filters = clone $this->filters; |
$filters->add(new SimpleFilter($format->getExtraParams(), 10)); |
|
if ($this->driver->getConfiguration()->has('ffmpeg.threads')) { |
$filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')))); |
} |
if ($format instanceof VideoInterface) { |
if (null !== $format->getVideoCodec()) { |
$filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec()))); |
} |
} |
if ($format instanceof AudioInterface) { |
if (null !== $format->getAudioCodec()) { |
$filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec()))); |
} |
} |
|
// Add the filters |
foreach ($this->filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this)); |
} |
|
if ($format instanceof AudioInterface) { |
if (null !== $format->getAudioKiloBitrate()) { |
$commands[] = '-b:a'; |
$commands[] = $format->getAudioKiloBitrate() . 'k'; |
} |
if (null !== $format->getAudioChannels()) { |
$commands[] = '-ac'; |
$commands[] = $format->getAudioChannels(); |
} |
} |
|
// If the user passed some additional parameters |
if ($format instanceof VideoInterface) { |
if (null !== $format->getAdditionalParameters()) { |
foreach ($format->getAdditionalParameters() as $additionalParameter) { |
$commands[] = $additionalParameter; |
} |
} |
} |
|
// Set the output file in the command |
$commands[] = $outputPathfile; |
|
$failure = null; |
|
try { |
$this->driver->command($commands); |
} catch (ExecutionFailureException $e) { |
throw new RuntimeException('Encoding failed', $e->getCode(), $e); |
} |
|
return $this; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/Frame.php |
@@ -0,0 +1,133 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use FFMpeg\Filters\Frame\FrameFilterInterface; |
use FFMpeg\Filters\Frame\FrameFilters; |
use FFMpeg\Driver\FFMpegDriver; |
use FFMpeg\FFProbe; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Coordinate\TimeCode; |
|
class Frame extends AbstractMediaType |
{ |
/** @var TimeCode */ |
private $timecode; |
/** @var Video */ |
private $video; |
|
public function __construct(Video $video, FFMpegDriver $driver, FFProbe $ffprobe, TimeCode $timecode) |
{ |
parent::__construct($video->getPathfile(), $driver, $ffprobe); |
$this->timecode = $timecode; |
$this->video = $video; |
} |
|
/** |
* Returns the video related to the frame. |
* |
* @return Video |
*/ |
public function getVideo() |
{ |
return $this->video; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return FrameFilters |
*/ |
public function filters() |
{ |
return new FrameFilters($this); |
} |
|
/** |
* {@inheritdoc} |
* |
* @return Frame |
*/ |
public function addFilter(FrameFilterInterface $filter) |
{ |
$this->filters->add($filter); |
|
return $this; |
} |
|
/** |
* @return TimeCode |
*/ |
public function getTimeCode() |
{ |
return $this->timecode; |
} |
|
/** |
* Saves the frame in the given filename. |
* |
* Uses the `unaccurate method by default.` |
* |
* @param string $pathfile |
* @param Boolean $accurate |
* |
* @return Frame |
* |
* @throws RuntimeException |
*/ |
public function save($pathfile, $accurate = false, $returnBase64 = false) |
{ |
/** |
* might be optimized with http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg |
* @see http://ffmpeg.org/ffmpeg.html#Main-options |
*/ |
$outputFormat = $returnBase64 ? "image2pipe" : "image2"; |
if (!$accurate) { |
$commands = array( |
'-y', '-ss', (string) $this->timecode, |
'-i', $this->pathfile, |
'-vframes', '1', |
'-f', $outputFormat |
); |
} else { |
$commands = array( |
'-y', '-i', $this->pathfile, |
'-vframes', '1', '-ss', (string) $this->timecode, |
'-f', $outputFormat |
); |
} |
|
if($returnBase64) { |
array_push($commands, "-"); |
} |
|
foreach ($this->filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this)); |
} |
|
$commands = array_merge($commands, array($pathfile)); |
|
try { |
if(!$returnBase64) { |
$this->driver->command($commands); |
return $this; |
} |
else { |
return $this->driver->command($commands); |
} |
} catch (ExecutionFailureException $e) { |
$this->cleanupTemporaryFile($pathfile); |
throw new RuntimeException('Unable to save frame', $e->getCode(), $e); |
} |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/Gif.php |
@@ -0,0 +1,137 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Strime <contact@strime.io> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use FFMpeg\Filters\Gif\GifFilterInterface; |
use FFMpeg\Filters\Gif\GifFilters; |
use FFMpeg\Driver\FFMpegDriver; |
use FFMpeg\FFProbe; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Coordinate\TimeCode; |
use FFMpeg\Coordinate\Dimension; |
|
class Gif extends AbstractMediaType |
{ |
/** @var TimeCode */ |
private $timecode; |
/** @var Dimension */ |
private $dimension; |
/** @var integer */ |
private $duration; |
/** @var Video */ |
private $video; |
|
public function __construct(Video $video, FFMpegDriver $driver, FFProbe $ffprobe, TimeCode $timecode, Dimension $dimension, $duration = null) |
{ |
parent::__construct($video->getPathfile(), $driver, $ffprobe); |
$this->timecode = $timecode; |
$this->dimension = $dimension; |
$this->duration = $duration; |
$this->video = $video; |
} |
|
/** |
* Returns the video related to the gif. |
* |
* @return Video |
*/ |
public function getVideo() |
{ |
return $this->video; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return GifFilters |
*/ |
public function filters() |
{ |
return new GifFilters($this); |
} |
|
/** |
* {@inheritdoc} |
* |
* @return Gif |
*/ |
public function addFilter(GifFilterInterface $filter) |
{ |
$this->filters->add($filter); |
|
return $this; |
} |
|
/** |
* @return TimeCode |
*/ |
public function getTimeCode() |
{ |
return $this->timecode; |
} |
|
/** |
* @return Dimension |
*/ |
public function getDimension() |
{ |
return $this->dimension; |
} |
|
/** |
* Saves the gif in the given filename. |
* |
* @param string $pathfile |
* |
* @return Gif |
* |
* @throws RuntimeException |
*/ |
public function save($pathfile) |
{ |
/** |
* @see http://ffmpeg.org/ffmpeg.html#Main-options |
*/ |
$commands = array( |
'-ss', (string)$this->timecode |
); |
|
if(null !== $this->duration) { |
$commands[] = '-t'; |
$commands[] = (string)$this->duration; |
} |
|
$commands[] = '-i'; |
$commands[] = $this->pathfile; |
$commands[] = '-vf'; |
$commands[] = 'scale=' . $this->dimension->getWidth() . ':-1'; |
$commands[] = '-gifflags'; |
$commands[] = '+transdiff'; |
$commands[] = '-y'; |
|
foreach ($this->filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this)); |
} |
|
$commands = array_merge($commands, array($pathfile)); |
|
try { |
$this->driver->command($commands); |
} catch (ExecutionFailureException $e) { |
$this->cleanupTemporaryFile($pathfile); |
throw new RuntimeException('Unable to save gif', $e->getCode(), $e); |
} |
|
return $this; |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/Video.php |
@@ -0,0 +1,268 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use FFMpeg\Coordinate\TimeCode; |
use FFMpeg\Coordinate\Dimension; |
use FFMpeg\Filters\Audio\SimpleFilter; |
use FFMpeg\Exception\InvalidArgumentException; |
use FFMpeg\Exception\RuntimeException; |
use FFMpeg\Filters\Video\VideoFilters; |
use FFMpeg\Filters\FilterInterface; |
use FFMpeg\Format\FormatInterface; |
use FFMpeg\Format\ProgressableInterface; |
use FFMpeg\Format\AudioInterface; |
use FFMpeg\Format\VideoInterface; |
use Neutron\TemporaryFilesystem\Manager as FsManager; |
|
class Video extends Audio |
{ |
/** |
* {@inheritdoc} |
* |
* @return VideoFilters |
*/ |
public function filters() |
{ |
return new VideoFilters($this); |
} |
|
/** |
* {@inheritdoc} |
* |
* @return Video |
*/ |
public function addFilter(FilterInterface $filter) |
{ |
$this->filters->add($filter); |
|
return $this; |
} |
|
/** |
* Exports the video in the desired format, applies registered filters. |
* |
* @param FormatInterface $format |
* @param string $outputPathfile |
* |
* @return Video |
* |
* @throws RuntimeException |
*/ |
public function save(FormatInterface $format, $outputPathfile) |
{ |
$commands = array('-y', '-i', $this->pathfile); |
|
$filters = clone $this->filters; |
$filters->add(new SimpleFilter($format->getExtraParams(), 10)); |
|
if ($this->driver->getConfiguration()->has('ffmpeg.threads')) { |
$filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')))); |
} |
if ($format instanceof VideoInterface) { |
if (null !== $format->getVideoCodec()) { |
$filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec()))); |
} |
} |
if ($format instanceof AudioInterface) { |
if (null !== $format->getAudioCodec()) { |
$filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec()))); |
} |
} |
|
foreach ($filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this, $format)); |
} |
|
if ($format instanceof VideoInterface) { |
$commands[] = '-b:v'; |
$commands[] = $format->getKiloBitrate() . 'k'; |
$commands[] = '-refs'; |
$commands[] = '6'; |
$commands[] = '-coder'; |
$commands[] = '1'; |
$commands[] = '-sc_threshold'; |
$commands[] = '40'; |
$commands[] = '-flags'; |
$commands[] = '+loop'; |
$commands[] = '-me_range'; |
$commands[] = '16'; |
$commands[] = '-subq'; |
$commands[] = '7'; |
$commands[] = '-i_qfactor'; |
$commands[] = '0.71'; |
$commands[] = '-qcomp'; |
$commands[] = '0.6'; |
$commands[] = '-qdiff'; |
$commands[] = '4'; |
$commands[] = '-trellis'; |
$commands[] = '1'; |
} |
|
if ($format instanceof AudioInterface) { |
if (null !== $format->getAudioKiloBitrate()) { |
$commands[] = '-b:a'; |
$commands[] = $format->getAudioKiloBitrate() . 'k'; |
} |
if (null !== $format->getAudioChannels()) { |
$commands[] = '-ac'; |
$commands[] = $format->getAudioChannels(); |
} |
} |
|
// If the user passed some additional parameters |
if ($format instanceof VideoInterface) { |
if (null !== $format->getAdditionalParameters()) { |
foreach ($format->getAdditionalParameters() as $additionalParameter) { |
$commands[] = $additionalParameter; |
} |
} |
} |
|
// Merge Filters into one command |
$videoFilterVars = $videoFilterProcesses = []; |
for($i=0;$i<count($commands);$i++) { |
$command = $commands[$i]; |
if ( $command == '-vf' ) { |
$commandSplits = explode(";", $commands[$i + 1]); |
if ( count($commandSplits) == 1 ) { |
$commandSplit = $commandSplits[0]; |
$command = trim($commandSplit); |
if ( preg_match("/^\[in\](.*?)\[out\]$/is", $command, $match) ) { |
$videoFilterProcesses[] = $match[1]; |
} else { |
$videoFilterProcesses[] = $command; |
} |
} else { |
foreach($commandSplits as $commandSplit) { |
$command = trim($commandSplit); |
if ( preg_match("/^\[[^\]]+\](.*?)\[[^\]]+\]$/is", $command, $match) ) { |
$videoFilterProcesses[] = $match[1]; |
} else { |
$videoFilterVars[] = $command; |
} |
} |
} |
unset($commands[$i]); |
unset($commands[$i + 1]); |
$i++; |
} |
} |
$videoFilterCommands = $videoFilterVars; |
$lastInput = 'in'; |
foreach($videoFilterProcesses as $i => $process) { |
$command = '[' . $lastInput .']'; |
$command .= $process; |
$lastInput = 'p' . $i; |
if ( $i == count($videoFilterProcesses) - 1 ) { |
$command .= '[out]'; |
} else { |
$command .= '[' . $lastInput . ']'; |
} |
|
$videoFilterCommands[] = $command; |
} |
$videoFilterCommand = implode(";", $videoFilterCommands); |
|
if ( $videoFilterCommand ) { |
$commands[] = '-vf'; |
$commands[] = $videoFilterCommand; |
} |
|
$fs = FsManager::create(); |
$fsId = uniqid('ffmpeg-passes'); |
$passPrefix = $fs->createTemporaryDirectory(0777, 50, $fsId) . '/' . uniqid('pass-'); |
$passes = array(); |
$totalPasses = $format->getPasses(); |
|
if (1 > $totalPasses) { |
throw new InvalidArgumentException('Pass number should be a positive value.'); |
} |
|
for ($i = 1; $i <= $totalPasses; $i++) { |
$pass = $commands; |
|
if ($totalPasses > 1) { |
$pass[] = '-pass'; |
$pass[] = $i; |
$pass[] = '-passlogfile'; |
$pass[] = $passPrefix; |
} |
|
$pass[] = $outputPathfile; |
|
$passes[] = $pass; |
} |
|
$failure = null; |
|
foreach ($passes as $pass => $passCommands) { |
try { |
/** add listeners here */ |
$listeners = null; |
|
if ($format instanceof ProgressableInterface) { |
$listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses); |
} |
|
$this->driver->command($passCommands, false, $listeners); |
} catch (ExecutionFailureException $e) { |
$failure = $e; |
break; |
} |
} |
|
$fs->clean($fsId); |
|
if (null !== $failure) { |
throw new RuntimeException('Encoding failed', $failure->getCode(), $failure); |
} |
|
return $this; |
} |
|
/** |
* Gets the frame at timecode. |
* |
* @param TimeCode $at |
* @return Frame |
*/ |
public function frame(TimeCode $at) |
{ |
return new Frame($this, $this->driver, $this->ffprobe, $at); |
} |
|
/** |
* Extracts a gif from a sequence of the video. |
* |
* @param TimeCode $at |
* @param Dimension $dimension |
* @param integer $duration |
* @return Gif |
*/ |
public function gif(TimeCode $at, Dimension $dimension, $duration = null) |
{ |
return new Gif($this, $this->driver, $this->ffprobe, $at, $dimension, $duration); |
} |
|
/** |
* Concatenates a list of videos into one unique video. |
* |
* @param array $sources |
* @return Concat |
*/ |
public function concat($sources) |
{ |
return new Concat($sources, $this->driver, $this->ffprobe); |
} |
} |
/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/Waveform.php |
@@ -0,0 +1,104 @@ |
<?php |
|
/* |
* This file is part of PHP-FFmpeg. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace FFMpeg\Media; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use FFMpeg\Filters\Waveform\WaveformFilterInterface; |
use FFMpeg\Filters\Waveform\WaveformFilters; |
use FFMpeg\Driver\FFMpegDriver; |
use FFMpeg\FFProbe; |
use FFMpeg\Exception\RuntimeException; |
|
class Waveform extends AbstractMediaType |
{ |
/** @var Video */ |
private $audio; |
private $width; |
private $height; |
|
public function __construct(Audio $audio, FFMpegDriver $driver, FFProbe $ffprobe, $width, $height) |
{ |
parent::__construct($audio->getPathfile(), $driver, $ffprobe); |
$this->audio = $audio; |
$this->width = $width; |
$this->height = $height; |
} |
|
/** |
* Returns the audio related to the waveform. |
* |
* @return Audio |
*/ |
public function getAudio() |
{ |
return $this->audio; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return WaveformFilters |
*/ |
public function filters() |
{ |
return new WaveformFilters($this); |
} |
|
/** |
* {@inheritdoc} |
* |
* @return Waveform |
*/ |
public function addFilter(WaveformFilterInterface $filter) |
{ |
$this->filters->add($filter); |
|
return $this; |
} |
|
/** |
* Saves the waveform in the given filename. |
* |
* @param string $pathfile |
* |
* @return Waveform |
* |
* @throws RuntimeException |
*/ |
public function save($pathfile) |
{ |
/** |
* might be optimized with http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg |
* @see http://ffmpeg.org/ffmpeg.html#Main-options |
*/ |
$commands = array( |
'-i', $this->pathfile, '-filter_complex', |
'showwavespic=s='.$this->width.'x'.$this->height, |
'-frames:v', '1' |
); |
|
foreach ($this->filters as $filter) { |
$commands = array_merge($commands, $filter->apply($this)); |
} |
|
$commands = array_merge($commands, array($pathfile)); |
|
try { |
$this->driver->command($commands); |
} catch (ExecutionFailureException $e) { |
$this->cleanupTemporaryFile($pathfile); |
throw new RuntimeException('Unable to save waveform', $e->getCode(), $e); |
} |
|
return $this; |
} |
} |