diff --git a/src/Alchemy/BinaryDriver/AbstractBinary.php b/src/Alchemy/BinaryDriver/AbstractBinary.php deleted file mode 100644 index b5809f0..0000000 --- a/src/Alchemy/BinaryDriver/AbstractBinary.php +++ /dev/null @@ -1,218 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException; -use Alchemy\BinaryDriver\Exception\ExecutionFailureException; -use Alchemy\BinaryDriver\Listeners\Listeners; -use Alchemy\BinaryDriver\Listeners\ListenerInterface; -use Evenement\EventEmitter; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; -use Symfony\Component\Process\ExecutableFinder; -use Symfony\Component\Process\Process; - -abstract class AbstractBinary extends EventEmitter implements BinaryInterface -{ - /** @var ConfigurationInterface */ - protected $configuration; - - /** @var ProcessBuilderFactoryInterface */ - protected $factory; - - /** @var ProcessRunner */ - private $processRunner; - - /** @var Listeners */ - private $listenersManager; - - public function __construct(ProcessBuilderFactoryInterface $factory, LoggerInterface $logger, ConfigurationInterface $configuration) - { - $this->factory = $factory; - $this->configuration = $configuration; - $this->processRunner = new ProcessRunner($logger, $this->getName()); - $this->listenersManager = new Listeners(); - $this->applyProcessConfiguration(); - } - - /** - * {@inheritdoc} - */ - public function listen(ListenerInterface $listener) - { - $this->listenersManager->register($listener, $this); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function unlisten(ListenerInterface $listener) - { - $this->listenersManager->unregister($listener, $this); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getConfiguration() - { - return $this->configuration; - } - - /** - * {@inheritdoc} - * - * @return BinaryInterface - */ - public function setConfiguration(ConfigurationInterface $configuration) - { - $this->configuration = $configuration; - $this->applyProcessConfiguration(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getProcessBuilderFactory() - { - return $this->factory; - } - - /** - * {@inheritdoc} - * - * @return BinaryInterface - */ - public function setProcessBuilderFactory(ProcessBuilderFactoryInterface $factory) - { - $this->factory = $factory; - $this->applyProcessConfiguration(); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getProcessRunner() - { - return $this->processRunner; - } - - /** - * {@inheritdoc} - */ - public function setProcessRunner(ProcessRunnerInterface $runner) - { - $this->processRunner = $runner; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function command($command, $bypassErrors = false, $listeners = null) - { - if (!is_array($command)) { - $command = array($command); - } - - return $this->run($this->factory->create($command), $bypassErrors, $listeners); - } - - /** - * {@inheritdoc} - */ - public static function load($binaries, LoggerInterface $logger = null, $configuration = array()) - { - $finder = new ExecutableFinder(); - $binary = null; - $binaries = is_array($binaries) ? $binaries : array($binaries); - - foreach ($binaries as $candidate) { - if (file_exists($candidate) && is_executable($candidate)) { - $binary = $candidate; - break; - } - if (null !== $binary = $finder->find($candidate)) { - break; - } - } - - if (null === $binary) { - throw new ExecutableNotFoundException(sprintf( - 'Executable not found, proposed : %s', implode(', ', $binaries) - )); - } - - if (null === $logger) { - $logger = new NullLogger(); - } - - $configuration = $configuration instanceof ConfigurationInterface ? $configuration : new Configuration($configuration); - - return new static(new ProcessBuilderFactory($binary), $logger, $configuration); - } - - /** - * Returns the name of the driver - * - * @return string - */ - abstract public function getName(); - - /** - * Executes a process, logs events - * - * @param Process $process - * @param Boolean $bypassErrors Set to true to disable throwing ExecutionFailureExceptions - * @param ListenerInterface|array $listeners A listener or an array of listener to register for this unique run - * - * @return string The Process output - * - * @throws ExecutionFailureException in case of process failure. - */ - protected function run(Process $process, $bypassErrors = false, $listeners = null) - { - if (null !== $listeners) { - if (!is_array($listeners)) { - $listeners = array($listeners); - } - - $listenersManager = clone $this->listenersManager; - - foreach ($listeners as $listener) { - $listenersManager->register($listener, $this); - } - } else { - $listenersManager = $this->listenersManager; - } - - return $this->processRunner->run($process, $listenersManager->storage, $bypassErrors); - } - - private function applyProcessConfiguration() - { - if ($this->configuration->has('timeout')) { - $this->factory->setTimeout($this->configuration->get('timeout')); - } - - return $this; - } -} diff --git a/src/Alchemy/BinaryDriver/BinaryDriverTestCase.php b/src/Alchemy/BinaryDriver/BinaryDriverTestCase.php deleted file mode 100644 index bd3cd81..0000000 --- a/src/Alchemy/BinaryDriver/BinaryDriverTestCase.php +++ /dev/null @@ -1,77 +0,0 @@ -getMockBuilder('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface')->getMock(); - } - - /** - * @param integer $runs The number of runs expected - * @param Boolean $success True if the process expects to be successfull - * @param string $commandLine The commandline executed - * @param string $output The process output - * @param string $error The process error output - * - * @return Process - */ - public function createProcessMock($runs = 1, $success = true, $commandLine = null, $output = null, $error = null, $callback = false) - { - $process = $this->getMockBuilder('Symfony\Component\Process\Process') - ->disableOriginalConstructor() - ->getMock(); - - $builder = $process->expects($this->exactly($runs)) - ->method('run'); - - if (true === $callback) { - $builder->with($this->isInstanceOf('Closure')); - } - - $process->expects($this->any()) - ->method('isSuccessful') - ->will($this->returnValue($success)); - - foreach ([ - 'getOutput' => $output, - 'getErrorOutput' => $error ?: "", - 'getCommandLine' => $commandLine, - ] as $command => $value) { - $process - ->expects($this->any()) - ->method($command) - ->will($this->returnValue($value)); - } - - return $process; - } - - /** - * @return LoggerInterface - */ - public function createLoggerMock() - { - return $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); - } - - /** - * @return ConfigurationInterface - */ - public function createConfigurationMock() - { - return $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock(); - } -} diff --git a/src/Alchemy/BinaryDriver/BinaryInterface.php b/src/Alchemy/BinaryDriver/BinaryInterface.php deleted file mode 100644 index 0dfd6e0..0000000 --- a/src/Alchemy/BinaryDriver/BinaryInterface.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException; -use Alchemy\BinaryDriver\Exception\ExecutionFailureException; -use Alchemy\BinaryDriver\Listeners\ListenerInterface; -use Psr\Log\LoggerInterface; -use Evenement\EventEmitterInterface; - -interface BinaryInterface extends ConfigurationAwareInterface, ProcessBuilderFactoryAwareInterface, ProcessRunnerAwareInterface, EventEmitterInterface -{ - /** - * Adds a listener to the binary driver - * - * @param ListenerInterface $listener - * - * @return BinaryInterface - */ - public function listen(ListenerInterface $listener); - - /** - * Removes a listener from the binary driver - * - * @param ListenerInterface $listener - * - * @return BinaryInterface - */ - public function unlisten(ListenerInterface $listener); - - /** - * Runs a command against the driver. - * - * Calling this method on a `ls` driver with the command `-a` would run `ls -a`. - * - * @param array|string $command A command or an array of command - * @param Boolean $bypassErrors If set to true, an erronous process will not throw an exception - * @param ListenerInterface|array $listeners A listener or an array of listeners to register for this unique run - * - * @return string The command output - * - * @throws ExecutionFailureException in case of process failure. - */ - public function command($command, $bypassErrors = false, $listeners = null); - - /** - * Loads a binary - * - * @param string|array $binaries A binary name or an array of binary names - * @param null|LoggerInterface $logger A Logger - * @param array|ConfigurationInterface $configuration The configuration - * - * @throws ExecutableNotFoundException In case none of the binaries were found - * - * @return BinaryInterface - */ - public static function load($binaries, LoggerInterface $logger = null, $configuration = array()); -} diff --git a/src/Alchemy/BinaryDriver/Configuration.php b/src/Alchemy/BinaryDriver/Configuration.php deleted file mode 100644 index 747195b..0000000 --- a/src/Alchemy/BinaryDriver/Configuration.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Traversable; - -class Configuration implements ConfigurationInterface -{ - private $data; - - public function __construct(array $data = []) - { - $this->data = $data; - } - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - return new \ArrayIterator($this->data); - } - - /** - * {@inheritdoc} - */ - public function get($key, $default = null) - { - return isset($this->data[$key]) ? $this->data[$key] : $default; - } - - /** - * {@inheritdoc} - */ - public function set($key, $value) - { - $this->data[$key] = $value; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function has($key) - { - return array_key_exists($key, $this->data); - } - - /** - * {@inheritdoc} - */ - public function remove($key) - { - $value = $this->get($key); - unset($this->data[$key]); - - return $value; - } - - /** - * {@inheritdoc} - */ - public function all() - { - return $this->data; - } - - /** - * {@inheritdoc} - */ - public function offsetExists($offset): bool - { - return $this->has($offset); - } - - /** - * {@inheritdoc} - */ - public function offsetGet($offset): mixed - { - return $this->get($offset); - } - - /** - * {@inheritdoc} - */ - public function offsetSet($offset, $value): void - { - $this->set($offset, $value); - } - - /** - * {@inheritdoc} - */ - public function offsetUnset($offset): void - { - $this->remove($offset); - } -} diff --git a/src/Alchemy/BinaryDriver/ConfigurationAwareInterface.php b/src/Alchemy/BinaryDriver/ConfigurationAwareInterface.php deleted file mode 100644 index 3b14f9e..0000000 --- a/src/Alchemy/BinaryDriver/ConfigurationAwareInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -interface ConfigurationAwareInterface -{ - /** - * Returns the configuration - * - * @return ConfigurationInterface - */ - public function getConfiguration(); - - /** - * Set the configuration - * - * @param ConfigurationInterface $configuration - */ - public function setConfiguration(ConfigurationInterface $configuration); -} diff --git a/src/Alchemy/BinaryDriver/ConfigurationInterface.php b/src/Alchemy/BinaryDriver/ConfigurationInterface.php deleted file mode 100644 index 71bcb88..0000000 --- a/src/Alchemy/BinaryDriver/ConfigurationInterface.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -interface ConfigurationInterface extends \ArrayAccess, \IteratorAggregate -{ - /** - * Returns the value given a key from configuration - * - * @param string $key - * @param mixed $default The default value in case the key does not exist - * - * @return mixed - */ - public function get($key, $default = null); - - /** - * Set a value to configuration - * - * @param string $key The key - * @param mixed $value The value corresponding to the key - */ - public function set($key, $value); - - /** - * Tells if Configuration contains `$key` - * - * @param string $key - * - * @return Boolean - */ - public function has($key); - - /** - * Removes a value given a key - * - * @param string $key - * - * @return mixed The previous value - */ - public function remove($key); - - /** - * Returns all values set in the configuration - * - * @return array - */ - public function all(); -} diff --git a/src/Alchemy/BinaryDriver/Exception/ExceptionInterface.php b/src/Alchemy/BinaryDriver/Exception/ExceptionInterface.php deleted file mode 100644 index aaa1e32..0000000 --- a/src/Alchemy/BinaryDriver/Exception/ExceptionInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver\Exception; - -interface ExceptionInterface -{ -} diff --git a/src/Alchemy/BinaryDriver/Exception/ExecutableNotFoundException.php b/src/Alchemy/BinaryDriver/Exception/ExecutableNotFoundException.php deleted file mode 100644 index 6f267ea..0000000 --- a/src/Alchemy/BinaryDriver/Exception/ExecutableNotFoundException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver\Exception; - -class ExecutableNotFoundException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/src/Alchemy/BinaryDriver/Exception/ExecutionFailureException.php b/src/Alchemy/BinaryDriver/Exception/ExecutionFailureException.php deleted file mode 100644 index 1b557d1..0000000 --- a/src/Alchemy/BinaryDriver/Exception/ExecutionFailureException.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver\Exception; - -class ExecutionFailureException extends \RuntimeException implements ExceptionInterface -{ - /** @var string */ - protected $command; - - /** @var string */ - protected $errorOutput; - - public function __construct($binaryName, $command, $errorOutput = null, $code = 0, $previous = null) - { - $message = sprintf("%s failed to execute command %s:\n\nError Output:\n\n %s", $binaryName, $command, $errorOutput); - parent::__construct($message, $code, $previous); - $this->command = $command; - $this->errorOutput = $errorOutput; - } - - public function getCommand(){ - return $this->command; - } - - public function getErrorOutput(){ - return $this->errorOutput; - } -} diff --git a/src/Alchemy/BinaryDriver/Exception/InvalidArgumentException.php b/src/Alchemy/BinaryDriver/Exception/InvalidArgumentException.php deleted file mode 100644 index 4e9cfe4..0000000 --- a/src/Alchemy/BinaryDriver/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver\Exception; - -class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface -{ -} diff --git a/src/Alchemy/BinaryDriver/Listeners/DebugListener.php b/src/Alchemy/BinaryDriver/Listeners/DebugListener.php deleted file mode 100644 index 20773b0..0000000 --- a/src/Alchemy/BinaryDriver/Listeners/DebugListener.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver\Listeners; - -use Evenement\EventEmitter; -use Symfony\Component\Process\Process; - -class DebugListener extends EventEmitter implements ListenerInterface -{ - private $prefixOut; - private $prefixErr; - private $eventOut; - private $eventErr; - - public function __construct($prefixOut = '[OUT] ', $prefixErr = '[ERROR] ', $eventOut = 'debug', $eventErr = 'debug') - { - $this->prefixOut = $prefixOut; - $this->prefixErr = $prefixErr; - $this->eventOut = $eventOut; - $this->eventErr = $eventErr; - } - - /** - * {@inheritdoc} - */ - public function handle($type, $data) - { - if (Process::ERR === $type) { - $this->emitLines($this->eventErr, $this->prefixErr, $data); - } elseif (Process::OUT === $type) { - $this->emitLines($this->eventOut, $this->prefixOut, $data); - } - } - - /** - * {@inheritdoc} - */ - public function forwardedEvents() - { - return array_unique(array($this->eventErr, $this->eventOut)); - } - - private function emitLines($event, $prefix, $lines) - { - foreach (explode("\n", $lines) as $line) { - $this->emit($event, array($prefix . $line)); - } - } -} diff --git a/src/Alchemy/BinaryDriver/Listeners/ListenerInterface.php b/src/Alchemy/BinaryDriver/Listeners/ListenerInterface.php deleted file mode 100644 index 920a6d5..0000000 --- a/src/Alchemy/BinaryDriver/Listeners/ListenerInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver\Listeners; - -use Evenement\EventEmitterInterface; - -interface ListenerInterface extends EventEmitterInterface -{ - /** - * Handle the output of a ProcessRunner - * - * @param string $type The data type, one of Process::ERR, Process::OUT constants - * @param string $data The output - */ - public function handle($type, $data); - - /** - * An array of events that should be forwarded to BinaryInterface - * - * @return array - */ - public function forwardedEvents(); -} diff --git a/src/Alchemy/BinaryDriver/Listeners/Listeners.php b/src/Alchemy/BinaryDriver/Listeners/Listeners.php deleted file mode 100644 index afb7549..0000000 --- a/src/Alchemy/BinaryDriver/Listeners/Listeners.php +++ /dev/null @@ -1,88 +0,0 @@ -storage = new SplObjectStorage(); - } - - public function __clone() - { - $storage = $this->storage; - $this->storage = new SplObjectStorage(); - $this->storage->addAll($storage); - } - - /** - * Registers a listener, pass the listener events to the target. - * - * @param ListenerInterface $listener - * @param null|EventEmitter $target - * - * @return ListenersInterface - */ - public function register(ListenerInterface $listener, EventEmitter $target = null) - { - $EElisteners = array(); - - if (null !== $target) { - $EElisteners = $this->forwardEvents($listener, $target, $listener->forwardedEvents()); - } - - $this->storage->attach($listener, $EElisteners); - - return $this; - } - - /** - * Unregisters a listener, removes the listener events from the target. - * - * @param ListenerInterface $listener - * - * @return ListenersInterface - * - * @throws InvalidArgumentException In case the listener is not registered - */ - public function unregister(ListenerInterface $listener) - { - if (!isset($this->storage[$listener])) { - throw new InvalidArgumentException('Listener is not registered.'); - } - - foreach ($this->storage[$listener] as $event => $EElistener) { - $listener->removeListener($event, $EElistener); - } - - $this->storage->detach($listener); - - return $this; - } - - private function forwardEvents($source, $target, array $events) - { - $EElisteners = array(); - - foreach ($events as $event) { - $listener = $this->createListener($event, $target); - $source->on($event, $EElisteners[$event] = $listener); - } - - return $EElisteners; - } - - private function createListener($event, $target) - { - return function () use ($event, $target) { - $target->emit($event, func_get_args()); - }; - } -} diff --git a/src/Alchemy/BinaryDriver/ProcessBuilderFactory.php b/src/Alchemy/BinaryDriver/ProcessBuilderFactory.php deleted file mode 100644 index b25ba3c..0000000 --- a/src/Alchemy/BinaryDriver/ProcessBuilderFactory.php +++ /dev/null @@ -1,186 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Alchemy\BinaryDriver\Exception\InvalidArgumentException; -use Symfony\Component\Process\Process; -use Symfony\Component\Process\ProcessBuilder; - -class ProcessBuilderFactory implements ProcessBuilderFactoryInterface -{ - /** - * The binary path - * - * @var String - */ - protected $binary; - - /** - * The timeout for the generated processes - * - * @var integer|float - */ - private $timeout; - - /** - * An internal ProcessBuilder. - * - * Note that this one is used only if Symfony ProcessBuilder has method - * setPrefix (2.3) - * - * @var ProcessBuilder - */ - private $builder; - - /** - * Tells whether Symfony LTS ProcessBuilder should be emulated or not. - * - * This symfony version provided a brand new ::setPrefix method. - * - * @var Boolean - */ - public static $emulateSfLTS; - - /** - * Constructor - * - * @param String $binary The path to the binary - * - * @throws InvalidArgumentException In case binary path is invalid - */ - public function __construct($binary) - { - $this->detectEmulation(); - - if (!self::$emulateSfLTS) { - $this->builder = new ProcessBuilder(); - } - - $this->useBinary($binary); - } - - /** - * Covenient method for unit testing - * - * @return type - */ - public function getBuilder() - { - return $this->builder; - } - - /** - * Covenient method for unit testing - * - * @param ProcessBuilder $builder - * @return ProcessBuilderFactory - */ - public function setBuilder(ProcessBuilder $builder) - { - $this->builder = $builder; - - return $this; - } - - /** - * @inheritdoc - */ - public function getBinary() - { - return $this->binary; - } - - /** - * @inheritdoc - */ - public function useBinary($binary) - { - if (!is_executable($binary)) { - throw new InvalidArgumentException(sprintf('`%s` is not an executable binary', $binary)); - } - - $this->binary = $binary; - - if (!static::$emulateSfLTS) { - $this->builder->setPrefix($binary); - } - - return $this; - } - - /** - * @inheritdoc - */ - public function setTimeout($timeout) - { - $this->timeout = $timeout; - - if (!static::$emulateSfLTS) { - $this->builder->setTimeout($this->timeout); - } - - return $this; - } - - /** - * @inheritdoc - */ - public function getTimeout() - { - return $this->timeout; - } - - /** - * @inheritdoc - */ - public function create($arguments = array()) - { - if (null === $this->binary) { - throw new InvalidArgumentException('No binary set'); - } - - if (!is_array($arguments)) { - $arguments = array($arguments); - } - - if (static::$emulateSfLTS) { - array_unshift($arguments, $this->binary); - if (method_exists('Symfony\Component\Process\ProcessUtils', 'escapeArgument')) { - $script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), $arguments)); - } else { - $script = $arguments; - } - - $env = array_replace($_ENV, $_SERVER); - $env = array_filter($env, function ($value) { - return !is_array($value); - }); - - return new Process($script, null, $env, null, $this->timeout); - } else { - return $this->builder - ->setArguments($arguments) - ->getProcess(); - } - } - - private function detectEmulation() - { - if (null !== static::$emulateSfLTS) { - return $this; - } - - static::$emulateSfLTS = !method_exists('Symfony\Component\Process\ProcessBuilder', 'setPrefix'); - - return $this; - } -} diff --git a/src/Alchemy/BinaryDriver/ProcessBuilderFactoryAwareInterface.php b/src/Alchemy/BinaryDriver/ProcessBuilderFactoryAwareInterface.php deleted file mode 100644 index 1398bb4..0000000 --- a/src/Alchemy/BinaryDriver/ProcessBuilderFactoryAwareInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -interface ProcessBuilderFactoryAwareInterface -{ - /** - * Returns the current process builder factory - * - * @return ProcessBuilderFactoryInterface - */ - public function getProcessBuilderFactory(); - - /** - * Set a process builder factory - * - * @param ProcessBuilderFactoryInterface $factory - */ - public function setProcessBuilderFactory(ProcessBuilderFactoryInterface $factory); -} diff --git a/src/Alchemy/BinaryDriver/ProcessBuilderFactoryInterface.php b/src/Alchemy/BinaryDriver/ProcessBuilderFactoryInterface.php deleted file mode 100644 index 05a2296..0000000 --- a/src/Alchemy/BinaryDriver/ProcessBuilderFactoryInterface.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Alchemy\BinaryDriver\Exception\InvalidArgumentException; -use Symfony\Component\Process\Process; - -interface ProcessBuilderFactoryInterface -{ - /** - * Returns a new instance of Symfony Process - * - * @param string|array $arguments An argument or an array of arguments - * - * @return Process - * - * @throws InvalidArgumentException - */ - public function create($arguments = array()); - - /** - * Returns the path to the binary that is used - * - * @return String - */ - public function getBinary(); - - /** - * Sets the path to the binary - * - * @param String $binary A path to a binary - * - * @return ProcessBuilderFactoryInterface - * - * @throws InvalidArgumentException In case binary is not executable - */ - public function useBinary($binary); - - /** - * Set the default timeout to apply on created processes. - * - * @param integer|float $timeout - * - * @return ProcessBuilderFactoryInterface - * - * @throws InvalidArgumentException In case the timeout is not valid - */ - public function setTimeout($timeout); - - /** - * Returns the current timeout applied to the created processes. - * - * @return integer|float - */ - public function getTimeout(); -} diff --git a/src/Alchemy/BinaryDriver/ProcessRunner.php b/src/Alchemy/BinaryDriver/ProcessRunner.php deleted file mode 100644 index 57ed987..0000000 --- a/src/Alchemy/BinaryDriver/ProcessRunner.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Alchemy\BinaryDriver\Exception\ExecutionFailureException; -use Psr\Log\LoggerInterface; -use SplObjectStorage; -use Symfony\Component\Process\Exception\RuntimeException; -use Symfony\Component\Process\Process; - -class ProcessRunner implements ProcessRunnerInterface -{ - /** @var LoggerInterface */ - private $logger; - - /** @var string */ - private $name; - - public function __construct(LoggerInterface $logger, $name) - { - $this->logger = $logger; - $this->name = $name; - } - - /** - * {@inheritdoc} - * - * @return void - */ - public function setLogger(LoggerInterface $logger): void - { - $this->logger = $logger; - } - - /** - * @return LoggerInterface - */ - public function getLogger() - { - return $this->logger; - } - - /** - * {@inheritdoc} - */ - public function run(Process $process, SplObjectStorage $listeners, $bypassErrors) - { - $this->logger->info(sprintf( - '%s running command %s', - $this->name, - $process->getCommandLine() - )); - - try { - $process->run($this->buildCallback($listeners)); - } catch (RuntimeException $e) { - if (!$bypassErrors) { - $this->doExecutionFailure($process->getCommandLine(), $process->getErrorOutput(), $e); - } - } - - if (!$bypassErrors && !$process->isSuccessful()) { - $this->doExecutionFailure($process->getCommandLine(), $process->getErrorOutput()); - } elseif (!$process->isSuccessful()) { - $this->logger->error($this->createErrorMessage($process->getCommandLine(), $process->getErrorOutput())); - return; - } else { - $this->logger->info(sprintf('%s executed command successfully', $this->name)); - return $process->getOutput(); - } - } - - private function buildCallback(SplObjectStorage $listeners) - { - return function ($type, $data) use ($listeners) { - foreach ($listeners as $listener) { - $listener->handle($type, $data); - } - }; - } - - private function doExecutionFailure($command, $errorOutput, \Exception $e = null) - { - $this->logger->error($this->createErrorMessage($command, $errorOutput)); - throw new ExecutionFailureException( - $this->name, - $command, - $errorOutput, - $e ? $e->getCode() : 0, - $e ?: null - ); - } - - private function createErrorMessage($command, $errorOutput) - { - return sprintf('%s failed to execute command %s: %s', $this->name, $command, $errorOutput); - } -} diff --git a/src/Alchemy/BinaryDriver/ProcessRunnerAwareInterface.php b/src/Alchemy/BinaryDriver/ProcessRunnerAwareInterface.php deleted file mode 100644 index 807c33e..0000000 --- a/src/Alchemy/BinaryDriver/ProcessRunnerAwareInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -interface ProcessRunnerAwareInterface -{ - /** - * Returns the current process runner - * - * @return ProcessRunnerInterface - */ - public function getProcessRunner(); - - /** - * Sets a process runner - * - * @param ProcessRunnerInterface $runner - */ - public function setProcessRunner(ProcessRunnerInterface $runner); -} diff --git a/src/Alchemy/BinaryDriver/ProcessRunnerInterface.php b/src/Alchemy/BinaryDriver/ProcessRunnerInterface.php deleted file mode 100644 index 6605404..0000000 --- a/src/Alchemy/BinaryDriver/ProcessRunnerInterface.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\BinaryDriver; - -use Alchemy\BinaryDriver\Exception\ExecutionFailureException; -use Psr\Log\LoggerAwareInterface; -use SplObjectStorage; -use Symfony\Component\Process\Process; - -interface ProcessRunnerInterface extends LoggerAwareInterface -{ - /** - * Executes a process, logs events - * - * @param Process $process - * @param SplObjectStorage $listeners Some listeners - * @param Boolean $bypassErrors Set to true to disable throwing ExecutionFailureExceptions - * - * @return string The Process output - * - * @throws ExecutionFailureException in case of process failure. - */ - public function run(Process $process, SplObjectStorage $listeners, $bypassErrors); -} diff --git a/src/FFMpeg/Coordinate/AspectRatio.php b/src/FFMpeg/Coordinate/AspectRatio.php deleted file mode 100644 index 6e42f5e..0000000 --- a/src/FFMpeg/Coordinate/AspectRatio.php +++ /dev/null @@ -1,259 +0,0 @@ - - * - * 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 - public const AR_4_3 = '4/3'; - // named 16:9 or 1.77:1 HD video standard - public const AR_16_9 = '16/9'; - - // named 8:5 or 16:10 or 1.6:1 - public const AR_8_5 = '8/5'; - - // named 25:16 or 1.56:1 - public const AR_25_16 = '25/16'; - - // named 3:2 or 1.5:1 see http://en.wikipedia.org/wiki/135_film - public const AR_3_2 = '3/2'; - // named 5:3 or 1.66:1 see http://en.wikipedia.org/wiki/Super_16_mm - public const AR_5_3 = '5/3'; - - // mostly used in Photography - public const AR_5_4 = '5/4'; - public const AR_1_1 = '1/1'; - - // 1.85:1 US widescreen cinema standard see http://en.wikipedia.org/wiki/Widescreen#Film - public 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 - public const AR_2_DOT_39_1 = '2.39:1'; - - // Rotated constants - - // Rotated 4:3 - public const AR_ROTATED_3_4 = '3/4'; - // Rotated 16:9 - public const AR_ROTATED_9_16 = '9/16'; - - // Rotated 3:2 - public const AR_ROTATED_2_3 = '2/3'; - // Rotated 5:3 - public const AR_ROTATED_3_5 = '3/5'; - - // Rotated 5:4 - public const AR_ROTATED_4_5 = '4/5'; - - // Rotated 1.85 - public const AR_ROTATED_1_DOT_85 = '1/1.85'; - // Rotated 2.39 - public 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 int $height - * @param int $modulus - * - * @return int - */ - 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 int $width - * @param int $modulus - * - * @return int - */ - 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 bool $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_8_5: - return 8 / 5; - case static::AR_25_16: - return 25 / 16; - 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 = [ - static::AR_4_3 => static::valueFromName(static::AR_4_3), - static::AR_16_9 => static::valueFromName(static::AR_16_9), - static::AR_8_5 => static::valueFromName(static::AR_8_5), - static::AR_25_16 => static::valueFromName(static::AR_25_16), - 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; - } -} diff --git a/src/FFMpeg/Coordinate/Dimension.php b/src/FFMpeg/Coordinate/Dimension.php deleted file mode 100644 index 8a62b2a..0000000 --- a/src/FFMpeg/Coordinate/Dimension.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * 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; - -/** - * Dimension object, used for manipulating width and height couples. - */ -class Dimension -{ - private $width; - private $height; - - /** - * @param int $width - * @param int $height - * - * @throws InvalidArgumentException when one of the parameteres is invalid - */ - public function __construct($width, $height) - { - if ($width <= 0 || $height <= 0) { - throw new InvalidArgumentException('Width and height should be positive integer'); - } - - $this->width = (int) $width; - $this->height = (int) $height; - } - - /** - * Returns width. - * - * @return int - */ - public function getWidth() - { - return $this->width; - } - - /** - * Returns height. - * - * @return int - */ - public function getHeight() - { - return $this->height; - } - - /** - * Returns the ratio. - * - * @param bool $forceStandards Whether or not force the use of standards ratios; - * - * @return AspectRatio - */ - public function getRatio($forceStandards = true) - { - return AspectRatio::create($this, $forceStandards); - } -} diff --git a/src/FFMpeg/Coordinate/FrameRate.php b/src/FFMpeg/Coordinate/FrameRate.php deleted file mode 100644 index ed0c7b2..0000000 --- a/src/FFMpeg/Coordinate/FrameRate.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * 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 FrameRate -{ - private $value; - - public function __construct($value) - { - if ($value <= 0) { - throw new InvalidArgumentException('Invalid frame rate, must be positive value.'); - } - - $this->value = $value; - } - - /** - * @return float - */ - public function getValue() - { - return $this->value; - } -} diff --git a/src/FFMpeg/Coordinate/Point.php b/src/FFMpeg/Coordinate/Point.php deleted file mode 100644 index 380c0f9..0000000 --- a/src/FFMpeg/Coordinate/Point.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Coordinate; - -class Point -{ - private $x; - private $y; - - public function __construct($x, $y, $dynamic = false) - { - if ($dynamic) { - $this->x = $x; - $this->y = $y; - } else { - $this->x = (int) $x; - $this->y = (int) $y; - } - } - - /** - * @return int - */ - public function getX() - { - return $this->x; - } - - /** - * @return int - */ - public function getY() - { - return $this->y; - } -} diff --git a/src/FFMpeg/Coordinate/TimeCode.php b/src/FFMpeg/Coordinate/TimeCode.php deleted file mode 100644 index 0d80d8c..0000000 --- a/src/FFMpeg/Coordinate/TimeCode.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * 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(); - } -} diff --git a/src/FFMpeg/Driver/FFMpegDriver.php b/src/FFMpeg/Driver/FFMpegDriver.php deleted file mode 100644 index 4ae30ed..0000000 --- a/src/FFMpeg/Driver/FFMpegDriver.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * 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 FFMpeg\Exception\RuntimeException; -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 = []) - { - if (!$configuration instanceof ConfigurationInterface) { - $configuration = new Configuration($configuration); - } - - $binaries = $configuration->get('ffmpeg.binaries', ['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); - } - } - - /** - * Get ffmpeg version. - * - * @return string - * - * @throws RuntimeException - */ - public function getVersion() - { - preg_match('#version\s(\S+)#', $this->command('-version'), $version); - if (!isset($version[1])) { - throw new RuntimeException('Cannot to parse the ffmpeg version!'); - } - - return $version[1]; - } -} diff --git a/src/FFMpeg/Driver/FFProbeDriver.php b/src/FFMpeg/Driver/FFProbeDriver.php deleted file mode 100644 index eb35807..0000000 --- a/src/FFMpeg/Driver/FFProbeDriver.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * 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 FFProbeDriver extends AbstractBinary -{ - /** - * {@inheritdoc} - */ - public function getName() - { - return 'ffprobe'; - } - - /** - * Creates an FFProbeDriver. - * - * @param array|ConfigurationInterface $configuration - * @param LoggerInterface $logger - * - * @return FFProbeDriver - */ - public static function create($configuration, LoggerInterface $logger = null) - { - if (!$configuration instanceof ConfigurationInterface) { - $configuration = new Configuration($configuration); - } - - $binaries = $configuration->get('ffprobe.binaries', ['avprobe', 'ffprobe']); - - try { - return static::load($binaries, $logger, $configuration); - } catch (BinaryDriverExecutableNotFound $e) { - throw new ExecutableNotFoundException('Unable to load FFProbe', $e->getCode(), $e); - } - } -} diff --git a/src/FFMpeg/Exception/ExceptionInterface.php b/src/FFMpeg/Exception/ExceptionInterface.php deleted file mode 100644 index df1bfb0..0000000 --- a/src/FFMpeg/Exception/ExceptionInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Exception; - -interface ExceptionInterface -{ -} diff --git a/src/FFMpeg/Exception/ExecutableNotFoundException.php b/src/FFMpeg/Exception/ExecutableNotFoundException.php deleted file mode 100644 index 28736d8..0000000 --- a/src/FFMpeg/Exception/ExecutableNotFoundException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Exception; - -class ExecutableNotFoundException extends RuntimeException -{ -} diff --git a/src/FFMpeg/Exception/InvalidArgumentException.php b/src/FFMpeg/Exception/InvalidArgumentException.php deleted file mode 100644 index c2ef8bd..0000000 --- a/src/FFMpeg/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Exception; - -class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface -{ -} diff --git a/src/FFMpeg/Exception/LogicException.php b/src/FFMpeg/Exception/LogicException.php deleted file mode 100644 index b73b567..0000000 --- a/src/FFMpeg/Exception/LogicException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Exception; - -class LogicException extends \LogicException implements ExceptionInterface -{ -} diff --git a/src/FFMpeg/Exception/RuntimeException.php b/src/FFMpeg/Exception/RuntimeException.php deleted file mode 100644 index 14261da..0000000 --- a/src/FFMpeg/Exception/RuntimeException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Exception; - -class RuntimeException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/src/FFMpeg/FFMpeg.php b/src/FFMpeg/FFMpeg.php deleted file mode 100644 index f919a61..0000000 --- a/src/FFMpeg/FFMpeg.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * 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\AdvancedMedia; -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'); - } - - /** - * Opens multiple input sources. - * - * @param string[] $inputs array of files to be opened - * - * @return AdvancedMedia - */ - public function openAdvanced($inputs) - { - return new AdvancedMedia($inputs, $this->driver, $this->ffprobe); - } - - /** - * Creates a new FFMpeg instance. - * - * @param array|ConfigurationInterface $configuration - * @param LoggerInterface $logger - * @param FFProbe $probe - * - * @return FFMpeg - */ - public static function create($configuration = [], LoggerInterface $logger = null, FFProbe $probe = null) - { - if (null === $probe) { - $probe = FFProbe::create($configuration, $logger, null); - } - - return new static(FFMpegDriver::create($logger, $configuration), $probe); - } -} diff --git a/src/FFMpeg/FFProbe.php b/src/FFMpeg/FFProbe.php deleted file mode 100644 index 07271f7..0000000 --- a/src/FFMpeg/FFProbe.php +++ /dev/null @@ -1,287 +0,0 @@ - - * - * 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 FFMpeg\Driver\FFProbeDriver; -use FFMpeg\Exception\InvalidArgumentException; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\FFProbe\DataMapping\Format; -use FFMpeg\FFProbe\DataMapping\StreamCollection; -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 Psr\Cache\CacheItemPoolInterface; -use Psr\Log\LoggerInterface; -use Symfony\Component\Cache\Adapter\ArrayAdapter; - -class FFProbe -{ - public const TYPE_STREAMS = 'streams'; - public const TYPE_FORMAT = 'format'; - - /** @var CacheItemPoolInterface */ - private $cache; - /** @var OptionsTesterInterface */ - private $optionsTester; - /** @var OutputParserInterface */ - private $parser; - /** @var FFProbeDriver */ - private $ffprobe; - /** @var MapperInterface */ - private $mapper; - - public function __construct(FFProbeDriver $ffprobe, CacheItemPoolInterface $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; - } - - /** - * @return FFProbe - */ - public function setParser(OutputParserInterface $parser) - { - $this->parser = $parser; - - return $this; - } - - /** - * @return FFProbeDriver - */ - public function getFFProbeDriver() - { - return $this->ffprobe; - } - - /** - * @return FFProbe - */ - public function setFFProbeDriver(FFProbeDriver $ffprobe) - { - $this->ffprobe = $ffprobe; - - return $this; - } - - /** - * @return FFProbe - */ - public function setOptionsTester(OptionsTesterInterface $tester) - { - $this->optionsTester = $tester; - - return $this; - } - - /** - * @return OptionsTesterInterface - */ - public function getOptionsTester() - { - return $this->optionsTester; - } - - /** - * @param CacheItemPoolInterface $cache - * - * @return FFProbe - */ - public function setCache(CacheItemPoolInterface $cache) - { - $this->cache = $cache; - - return $this; - } - - /** - * @return Cache - */ - public function getCache() - { - return $this->cache; - } - - /** - * @return MapperInterface - */ - public function getMapper() - { - return $this->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 - * - * Checks wether the given `$pathfile` is considered a valid media file. - * - * @param string $pathfile - * - * @return bool - * - * @since 0.10.0 - */ - public function isValid($pathfile) - { - try { - return $this->format($pathfile)->get('duration') > 0; - } catch (\Exception $e) { - // complete invalid data - return false; - } - } - - /** - * @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 CacheItemPoolInterface $cache - * - * @return FFProbe - */ - public static function create($configuration = [], LoggerInterface $logger = null, CacheItemPoolInterface $cache = null) - { - if (null === $cache) { - $cache = new ArrayAdapter(); - } - - return new static(FFProbeDriver::create($configuration, $logger), $cache); - } - - private function probe($pathfile, $command, $type, $allowJson = true) - { - $id = md5(sprintf('%s-%s', $command, $pathfile)); - - if ($this->cache->hasItem($id)) { - return $this->cache->getItem($id)->get(); - } - - 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 = [$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); - - $cacheItem = $this->cache->getItem($id); - $cacheItem->set($ret); - $this->cache->save($cacheItem); - - 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; - } -} diff --git a/src/FFMpeg/FFProbe/DataMapping/AbstractData.php b/src/FFMpeg/FFProbe/DataMapping/AbstractData.php deleted file mode 100644 index c4e9a18..0000000 --- a/src/FFMpeg/FFProbe/DataMapping/AbstractData.php +++ /dev/null @@ -1,94 +0,0 @@ - - * - * 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 bool - */ - 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(): int - { - return count($this->properties); - } -} diff --git a/src/FFMpeg/FFProbe/DataMapping/Format.php b/src/FFMpeg/FFProbe/DataMapping/Format.php deleted file mode 100644 index 4d7caa0..0000000 --- a/src/FFMpeg/FFProbe/DataMapping/Format.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe\DataMapping; - -class Format extends AbstractData -{ -} diff --git a/src/FFMpeg/FFProbe/DataMapping/Stream.php b/src/FFMpeg/FFProbe/DataMapping/Stream.php deleted file mode 100644 index 8699872..0000000 --- a/src/FFMpeg/FFProbe/DataMapping/Stream.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * 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\Coordinate\Dimension; -use FFMpeg\Exception\LogicException; -use FFMpeg\Exception\RuntimeException; - -class Stream extends AbstractData -{ - /** - * Returns true if the stream is an audio stream. - * - * @return bool - */ - public function isAudio() - { - return 'audio' === $this->get('codec_type'); - } - - /** - * Returns true if the stream is a video stream. - * - * @return bool - */ - public function isVideo() - { - return 'video' === $this->get('codec_type'); - } - - /** - * 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 (1 !== $sampleRatio[0] && 1 !== $sampleRatio[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 array|null 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); - } - } - } -} diff --git a/src/FFMpeg/FFProbe/DataMapping/StreamCollection.php b/src/FFMpeg/FFProbe/DataMapping/StreamCollection.php deleted file mode 100644 index e2acc6e..0000000 --- a/src/FFMpeg/FFProbe/DataMapping/StreamCollection.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe\DataMapping; - -use Traversable; - -class StreamCollection implements \Countable, \IteratorAggregate -{ - private $streams; - - public function __construct(array $streams = []) - { - $this->streams = array_values($streams); - } - - /** - * Returns the first stream of the collection, null if the collection is - * empty. - * - * @return Stream|null - */ - public function first() - { - $stream = reset($this->streams); - - return $stream ?: null; - } - - /** - * Adds a stream to the collection. - * - * @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(): int - { - return count($this->streams); - } - - /** - * Returns the array of contained streams. - * - * @return array - */ - public function all() - { - return $this->streams; - } - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - return new \ArrayIterator($this->streams); - } -} diff --git a/src/FFMpeg/FFProbe/Mapper.php b/src/FFMpeg/FFProbe/Mapper.php deleted file mode 100644 index 60217eb..0000000 --- a/src/FFMpeg/FFProbe/Mapper.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe; - -use FFMpeg\Exception\InvalidArgumentException; -use FFMpeg\FFProbe; -use FFMpeg\FFProbe\DataMapping\Format; -use FFMpeg\FFProbe\DataMapping\Stream; -use FFMpeg\FFProbe\DataMapping\StreamCollection; - -class Mapper implements MapperInterface -{ - /** - * {@inheritdoc} - */ - public function map($type, $data) - { - switch ($type) { - case FFProbe::TYPE_FORMAT: - return $this->mapFormat($data); - case FFProbe::TYPE_STREAMS: - return $this->mapStreams($data); - default: - throw new InvalidArgumentException(sprintf('Invalid type `%s`.', $type)); - } - } - - private function mapFormat($data) - { - return new Format($data['format']); - } - - private function mapStreams($data) - { - $streams = new StreamCollection(); - - foreach ($data['streams'] as $properties) { - $streams->add(new Stream($properties)); - } - - return $streams; - } -} diff --git a/src/FFMpeg/FFProbe/MapperInterface.php b/src/FFMpeg/FFProbe/MapperInterface.php deleted file mode 100644 index dbd1312..0000000 --- a/src/FFMpeg/FFProbe/MapperInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe; - -interface MapperInterface -{ - /** - * Maps data given its type. - * - * @param string $type One of FFProbe::TYPE_* constant - * @param string $data The data - * - * @return Format|Stream - * - * @throws InvalidArgumentException In case the type is not supported - */ - public function map($type, $data); -} diff --git a/src/FFMpeg/FFProbe/OptionsTester.php b/src/FFMpeg/FFProbe/OptionsTester.php deleted file mode 100644 index 186b055..0000000 --- a/src/FFMpeg/FFProbe/OptionsTester.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * 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 FFMpeg\Driver\FFProbeDriver; -use FFMpeg\Exception\RuntimeException; -use Psr\Cache\CacheItemPoolInterface; - -class OptionsTester implements OptionsTesterInterface -{ - /** @var FFProbeDriver */ - private $ffprobe; - /** @var CacheItemPoolInterface */ - private $cache; - - public function __construct(FFProbeDriver $ffprobe, CacheItemPoolInterface $cache) - { - $this->ffprobe = $ffprobe; - $this->cache = $cache; - } - - /** - * {@inheritdoc} - */ - public function has($name) - { - $id = md5(sprintf('option-%s', $name)); - - if ($this->cache->hasItem($id)) { - return $this->cache->getItem($id)->get(); - } - - $output = $this->retrieveHelpOutput(); - - $ret = (bool) preg_match('/^'.$name.'/m', $output); - - $cacheItem = $this->cache->getItem($id); - $cacheItem->set($ret); - $this->cache->save($cacheItem); - - return $ret; - } - - private function retrieveHelpOutput() - { - $id = 'help'; - - if ($this->cache->hasItem($id)) { - return $this->cache->getItem($id)->get(); - } - - try { - $output = $this->ffprobe->command(['-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); - } - - $cacheItem = $this->cache->getItem($id); - $cacheItem->set($output); - $this->cache->save($cacheItem); - - return $output; - } -} diff --git a/src/FFMpeg/FFProbe/OptionsTesterInterface.php b/src/FFMpeg/FFProbe/OptionsTesterInterface.php deleted file mode 100644 index 04c4df0..0000000 --- a/src/FFMpeg/FFProbe/OptionsTesterInterface.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe; - -interface OptionsTesterInterface -{ - /** - * Tells if the given option is supported by ffprobe. - * - * @param string $name - * - * @return bool - */ - public function has($name); -} diff --git a/src/FFMpeg/FFProbe/OutputParser.php b/src/FFMpeg/FFProbe/OutputParser.php deleted file mode 100644 index 1fb620d..0000000 --- a/src/FFMpeg/FFProbe/OutputParser.php +++ /dev/null @@ -1,123 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe; - -use FFMpeg\Exception\InvalidArgumentException; -use FFMpeg\FFProbe; - -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 = []; - - foreach (explode(PHP_EOL, $data) as $line) { - if (in_array($line, ['[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'] = []; - } - $ret['tags'][substr($key, 4)] = $value; - } else { - $ret[$key] = $value; - } - } - - return ['format' => $ret]; - } - - private function parseStreams($data) - { - $ret = []; - $n = -1; - - foreach (explode(PHP_EOL, $data) as $line) { - if ('[STREAM]' == $line) { - ++$n; - $ret[$n] = []; - continue; - } - if ('[/STREAM]' == $line) { - 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, ['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'] = []; - } - $ret[$n]['tags'][substr($key, 4)] = $value; - } elseif (0 === strpos($key, 'DISPOSITION:')) { - if (!isset($ret[$n]['disposition'])) { - $ret[$n]['disposition'] = []; - } - $ret[$n]['disposition'][substr($key, 12)] = $value; - } else { - $ret[$n][$key] = $value; - } - } - - return ['streams' => $ret]; - } -} diff --git a/src/FFMpeg/FFProbe/OutputParserInterface.php b/src/FFMpeg/FFProbe/OutputParserInterface.php deleted file mode 100644 index f220fce..0000000 --- a/src/FFMpeg/FFProbe/OutputParserInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\FFProbe; - -interface OutputParserInterface -{ - /** - * Parses ffprobe raw output. - * - * @param string $type One of FFProbe::TYPE_* constant - * @param string $data The data - * - * @return array - * - * @throws InvalidArgumentException In case the type is not supported - */ - public function parse($type, $data); -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/ANullSrcFilter.php b/src/FFMpeg/Filters/AdvancedMedia/ANullSrcFilter.php deleted file mode 100644 index 4c5753b..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/ANullSrcFilter.php +++ /dev/null @@ -1,71 +0,0 @@ -channelLayout = $channelLayout; - $this->sampleRate = $sampleRate; - $this->nbSamples = $nbSamples; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return 'anullsrc'; - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return [ - '-filter_complex', - $this->getName().$this->buildFilterOptions([ - 'channel_layout' => $this->channelLayout, - 'sample_rate' => $this->sampleRate, - 'nb_samples' => $this->nbSamples, - ]), - ]; - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/AbstractComplexFilter.php b/src/FFMpeg/Filters/AdvancedMedia/AbstractComplexFilter.php deleted file mode 100644 index 59d2b6d..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/AbstractComplexFilter.php +++ /dev/null @@ -1,62 +0,0 @@ -priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - return '0.3'; - } - - /** - * Generate the config of the filter. - * - * @param array $params Associative array of filter options. The options may be null. - * - * @return string the string of the form "=name1=value1:name2=value2" or empty string - */ - protected function buildFilterOptions(array $params) - { - $config = []; - foreach ($params as $paramName => $paramValue) { - if (null !== $paramValue) { - $config[] = $paramName.'='.$paramValue; - } - } - - if (!empty($config)) { - return '='.implode(':', $config); - } - - return ''; - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/ComplexCompatibleFilter.php b/src/FFMpeg/Filters/AdvancedMedia/ComplexCompatibleFilter.php deleted file mode 100644 index 4c4461d..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/ComplexCompatibleFilter.php +++ /dev/null @@ -1,33 +0,0 @@ -priority = $baseFilter->getPriority(); - $this->inLabels = $inLabels; - $this->baseFilter = $baseFilter; - $this->outLabels = $outLabels; - } - - /** - * Returns the priority of the filter. - * - * @return int - */ - public function getPriority() - { - return $this->priority; - } - - /** - * @return string - */ - public function getInLabels() - { - return $this->inLabels; - } - - /** - * @return string - */ - public function getOutLabels() - { - return $this->outLabels; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return $this->baseFilter->getName(); - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - return $this->baseFilter->getMinimalFFMpegVersion(); - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return $this->baseFilter->applyComplex($media); - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/ComplexFilterInterface.php b/src/FFMpeg/Filters/AdvancedMedia/ComplexFilterInterface.php deleted file mode 100644 index 58568d6..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/ComplexFilterInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -media = $media; - } - - /** - * @param string $in - * @param string $parameters - * @param string $out - * - * @return ComplexFilters - */ - public function custom($in, $parameters, $out) - { - $this->media->addFilter($in, new CustomComplexFilter($parameters), $out); - - return $this; - } - - /** - * Adds padding (black bars) to a video. - * - * @param string $in - * @param string $out - * - * @return ComplexFilters - */ - public function pad($in, Dimension $dimension, $out) - { - $this->media->addFilter($in, new PadFilter($dimension), $out); - - return $this; - } - - /** - * Adds a watermark image to a video. - * - * @param string $in - * @param string $imagePath - * @param string $out - * - * @return $this - */ - public function watermark($in, $imagePath, $out, array $coordinates = []) - { - $this->media->addFilter($in, new WatermarkFilter($imagePath, $coordinates), $out); - - return $this; - } - - /** - * Apply "xstack" filter. - * Warning: this filter is supported starting from 4.1 ffmpeg version. - * - * @param string $in - * @param string $layout - * @param int $inputsCount - * @param string $out - * - * @return ComplexFilters - * - * @see https://ffmpeg.org/ffmpeg-filters.html#xstack - */ - public function xStack($in, $layout, $inputsCount, $out) - { - $this->media->addFilter($in, new XStackFilter($layout, $inputsCount), $out); - - return $this; - } - - /** - * This filter build various types of computed inputs. - * - * @param string $out - * @param string|null $type - * @param string|null $size - * @param string|null $duration - * @param string|null $sar - * @param string|null $rate - * @param string|null $level - * @param string|null $color - * @param int|null $alpha - * @param float|null $decimals - * - * @return ComplexFilters - * - * @see https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-haldclutsrc_002c-nullsrc_002c-pal75bars_002c-pal100bars_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc - */ - public function testSrc( - $out, - $type = TestSrcFilter::TESTSRC, - $size = '320x240', - $duration = null, - $sar = null, - $rate = null, - $level = null, - $color = null, - $alpha = null, - $decimals = null - ) { - $this->media->addFilter( - '', - new TestSrcFilter($type, $size, $duration, $sar, $rate, $level, $color, $alpha, $decimals), - $out - ); - - return $this; - } - - /** - * Apply "anullsrc" filter. - * - * @param string $out - * @param string|null $channelLayout - * @param int|null $sampleRate - * @param int|null $nbSamples - * - * @return ComplexFilters - * - * @see https://ffmpeg.org/ffmpeg-filters.html#anullsrc - */ - public function aNullSrc( - $out, - $channelLayout = null, - $sampleRate = null, - $nbSamples = null - ) { - $this->media->addFilter('', new ANullSrcFilter($channelLayout, $sampleRate, $nbSamples), $out); - - return $this; - } - - /** - * Apply "sine" filter. - * - * @param $out - * @param string $duration - * @param int|null $frequency - * @param string|null $beep_factor - * @param int|null $sample_rate - * @param string|null $samples_per_frame - * - * @return $this - * - * @see https://ffmpeg.org/ffmpeg-filters.html#sine - */ - public function sine( - $out, - $duration, - $frequency = null, - $beep_factor = null, - $sample_rate = null, - $samples_per_frame = null - ) { - $this->media->addFilter( - '', - new SineFilter($duration, $frequency, $beep_factor, $sample_rate, $samples_per_frame), - $out - ); - - return $this; - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/CustomComplexFilter.php b/src/FFMpeg/Filters/AdvancedMedia/CustomComplexFilter.php deleted file mode 100644 index 454e0ba..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/CustomComplexFilter.php +++ /dev/null @@ -1,43 +0,0 @@ -filter = $filter; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return 'custom_filter'; - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return ['-filter_complex', $this->filter]; - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/SineFilter.php b/src/FFMpeg/Filters/AdvancedMedia/SineFilter.php deleted file mode 100644 index 630e925..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/SineFilter.php +++ /dev/null @@ -1,95 +0,0 @@ -duration = $duration; - $this->frequency = $frequency; - $this->beep_factor = $beep_factor; - $this->sample_rate = $sample_rate; - $this->samples_per_frame = $samples_per_frame; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return 'sine'; - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - return '2.0'; - } - - /** - * Apply the complex filter to the given media. - * - * @return string[] an array of arguments - */ - public function applyComplex(AdvancedMedia $media) - { - return [ - '-filter_complex', - $this->getName().$this->buildFilterOptions([ - 'frequency' => $this->frequency, - 'beep_factor' => $this->beep_factor, - 'sample_rate' => $this->sample_rate, - 'duration' => $this->duration, - 'samples_per_frame' => $this->samples_per_frame, - ]), - ]; - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/TestSrcFilter.php b/src/FFMpeg/Filters/AdvancedMedia/TestSrcFilter.php deleted file mode 100644 index 53e7bc5..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/TestSrcFilter.php +++ /dev/null @@ -1,246 +0,0 @@ -type = $type; - $this->level = $level; - $this->color = $color; - $this->size = $size; - $this->rate = $rate; - $this->duration = $duration; - $this->sar = $sar; - $this->alpha = $alpha; - $this->decimals = $decimals; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return $this->type; - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - switch ($this->type) { - case self::PAL75BARS: - case self::PAL100BARS: - return '4.1'; - case self::YUVTESTSRC: - return '3.2'; - case self::ALLRGB: - case self::ALLYUV: - return '2.8'; - case self::SMPTEHDBARS: - return '2.0'; - case self::SMPTEBARS: - return '1.0'; - default: - return '0.3'; - } - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return [ - '-filter_complex', - $this->type.$this->buildFilterOptions([ - 'level' => $this->level, - 'color' => $this->color, - 'size' => $this->size, - 'rate' => $this->rate, - 'duration' => $this->duration, - 'sar' => $this->sar, - 'alpha' => $this->alpha, - 'decimals' => $this->decimals, - ]), - ]; - } -} diff --git a/src/FFMpeg/Filters/AdvancedMedia/XStackFilter.php b/src/FFMpeg/Filters/AdvancedMedia/XStackFilter.php deleted file mode 100644 index 91b2ec2..0000000 --- a/src/FFMpeg/Filters/AdvancedMedia/XStackFilter.php +++ /dev/null @@ -1,94 +0,0 @@ -layout = $layout; - $this->inputsCount = $inputsCount; - } - - /** - * @param int $count - * - * @return string - */ - public static function getInputByCount($count) - { - $result = ''; - for ($i = 0; $i < $count; ++$i) { - $result .= '['.$i.':v]'; - } - - return $result; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return 'xstack'; - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - return '4.1'; - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return [ - '-filter_complex', - $this->getName().$this->buildFilterOptions([ - 'inputs' => $this->inputsCount, - 'layout' => $this->layout, - ]), - ]; - } -} diff --git a/src/FFMpeg/Filters/Audio/AddMetadataFilter.php b/src/FFMpeg/Filters/Audio/AddMetadataFilter.php deleted file mode 100644 index daad712..0000000 --- a/src/FFMpeg/Filters/Audio/AddMetadataFilter.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * 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\Format\AudioInterface; -use FFMpeg\Media\Audio; - -class AddMetadataFilter implements AudioFilterInterface -{ - /** @var array */ - private $metaArr; - /** @var int */ - private $priority; - - public function __construct($metaArr = null, $priority = 9) - { - $this->metaArr = $metaArr; - $this->priority = $priority; - } - - public function getPriority() - { - //must be of high priority in case theres a second input stream (artwork) to register with audio - return $this->priority; - } - - public function apply(Audio $audio, AudioInterface $format) - { - $meta = $this->metaArr; - - if (is_null($meta)) { - return ['-map_metadata', '-1', '-vn']; - } - - $metadata = []; - - if (array_key_exists('artwork', $meta)) { - array_push($metadata, '-i', $meta['artwork'], '-map', '0', '-map', '1'); - unset($meta['artwork']); - } - - foreach ($meta as $k => $v) { - array_push($metadata, '-metadata', "$k=$v"); - } - - return $metadata; - } -} diff --git a/src/FFMpeg/Filters/Audio/AudioClipFilter.php b/src/FFMpeg/Filters/Audio/AudioClipFilter.php deleted file mode 100644 index dcf9ffc..0000000 --- a/src/FFMpeg/Filters/Audio/AudioClipFilter.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * 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\Coordinate\TimeCode; -use FFMpeg\Format\AudioInterface; -use FFMpeg\Media\Audio; - -class AudioClipFilter implements AudioFilterInterface -{ - /** - * @var TimeCode - */ - private $start; - - /** - * @var TimeCode - */ - private $duration; - - /** - * @var int - */ - private $priority; - - public function __construct(TimeCode $start, TimeCode $duration = null, $priority = 0) - { - $this->start = $start; - $this->duration = $duration; - $this->priority = $priority; - } - - /** - * {@inheritDoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * Returns the start position the audio is being cutted. - * - * @return TimeCode - */ - public function getStart() - { - return $this->start; - } - - /** - * Returns how long the audio is being cutted. Returns null when the duration is infinite,. - * - * @return TimeCode|null - */ - public function getDuration() - { - return $this->duration; - } - - /** - * {@inheritDoc} - */ - public function apply(Audio $audio, AudioInterface $format) - { - $commands = ['-ss', (string) $this->start]; - - if (null !== $this->duration) { - $commands[] = '-t'; - $commands[] = (string) $this->duration; - } - - $commands[] = '-acodec'; - $commands[] = 'copy'; - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Audio/AudioFilterInterface.php b/src/FFMpeg/Filters/Audio/AudioFilterInterface.php deleted file mode 100644 index b49388b..0000000 --- a/src/FFMpeg/Filters/Audio/AudioFilterInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * 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\FilterInterface; -use FFMpeg\Format\AudioInterface; -use FFMpeg\Media\Audio; - -interface AudioFilterInterface extends FilterInterface -{ - /** - * Applies the filter on the the Audio media given an format. - * - * @return array An array of arguments - */ - public function apply(Audio $audio, AudioInterface $format); -} diff --git a/src/FFMpeg/Filters/Audio/AudioFilters.php b/src/FFMpeg/Filters/Audio/AudioFilters.php deleted file mode 100644 index db18a49..0000000 --- a/src/FFMpeg/Filters/Audio/AudioFilters.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * 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\Coordinate\TimeCode; -use FFMpeg\Media\Audio; - -class AudioFilters -{ - protected $media; - - public function __construct(Audio $media) - { - $this->media = $media; - } - - /** - * Resamples the audio file. - * - * @param int $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; - } - - /** - * Applies a custom filter. - * - * @param string $parameters - * - * @return AudioFilters - */ - public function custom($parameters) - { - $this->media->addFilter(new CustomFilter($parameters)); - - return $this; - } -} diff --git a/src/FFMpeg/Filters/Audio/AudioResamplableFilter.php b/src/FFMpeg/Filters/Audio/AudioResamplableFilter.php deleted file mode 100644 index 4ac7767..0000000 --- a/src/FFMpeg/Filters/Audio/AudioResamplableFilter.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * 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\Format\AudioInterface; -use FFMpeg\Media\Audio; - -class AudioResamplableFilter implements AudioFilterInterface -{ - /** @var string */ - private $rate; - /** @var int */ - private $priority; - - public function __construct($rate, $priority = 0) - { - $this->rate = $rate; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * @return int - */ - public function getRate() - { - return $this->rate; - } - - /** - * {@inheritdoc} - */ - public function apply(Audio $audio, AudioInterface $format) - { - return ['-ac', 2, '-ar', $this->rate]; - } -} diff --git a/src/FFMpeg/Filters/Audio/CustomFilter.php b/src/FFMpeg/Filters/Audio/CustomFilter.php deleted file mode 100644 index f461699..0000000 --- a/src/FFMpeg/Filters/Audio/CustomFilter.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * 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\Format\AudioInterface; -use FFMpeg\Media\Audio; - -class CustomFilter implements AudioFilterInterface -{ - /** @var string */ - private $filter; - /** @var int */ - private $priority; - - /** - * A custom filter, useful if you want to build complex filters. - * - * @param string $filter - * @param int $priority - */ - public function __construct($filter, $priority = 0) - { - $this->filter = $filter; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Audio $audio, AudioInterface $format) - { - $commands = ['-af', $this->filter]; - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Audio/SimpleFilter.php b/src/FFMpeg/Filters/Audio/SimpleFilter.php deleted file mode 100644 index 7fbef3d..0000000 --- a/src/FFMpeg/Filters/Audio/SimpleFilter.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * 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\Format\AudioInterface; -use FFMpeg\Media\Audio; - -class SimpleFilter implements AudioFilterInterface -{ - private $params; - private $priority; - - public function __construct(array $params, $priority = 0) - { - $this->params = $params; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Audio $audio, AudioInterface $format) - { - return $this->params; - } -} diff --git a/src/FFMpeg/Filters/Concat/ConcatFilterInterface.php b/src/FFMpeg/Filters/Concat/ConcatFilterInterface.php deleted file mode 100644 index 33eea50..0000000 --- a/src/FFMpeg/Filters/Concat/ConcatFilterInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Concat; - -use FFMpeg\Filters\FilterInterface; -use FFMpeg\Media\Concat; - -interface ConcatFilterInterface extends FilterInterface -{ - public function apply(Concat $concat); -} diff --git a/src/FFMpeg/Filters/Concat/ConcatFilters.php b/src/FFMpeg/Filters/Concat/ConcatFilters.php deleted file mode 100644 index 5fe4e3e..0000000 --- a/src/FFMpeg/Filters/Concat/ConcatFilters.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Concat; - -use FFMpeg\Media\Concat; - -class ConcatFilters -{ - private $concat; - - public function __construct(Concat $concat) - { - $this->concat = $concat; - } -} diff --git a/src/FFMpeg/Filters/FilterInterface.php b/src/FFMpeg/Filters/FilterInterface.php deleted file mode 100644 index 5e2a040..0000000 --- a/src/FFMpeg/Filters/FilterInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters; - -interface FilterInterface -{ - /** - * Returns the priority of the filter. - * - * @return int - */ - public function getPriority(); -} diff --git a/src/FFMpeg/Filters/FiltersCollection.php b/src/FFMpeg/Filters/FiltersCollection.php deleted file mode 100644 index eb33c83..0000000 --- a/src/FFMpeg/Filters/FiltersCollection.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters; - -use Traversable; - -class FiltersCollection implements \Countable, \IteratorAggregate -{ - private $sorted; - private $filters = []; - - /** - * @return FiltersCollection - */ - public function add(FilterInterface $filter) - { - $this->filters[$filter->getPriority()][] = $filter; - $this->sorted = null; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function count(): int - { - if (0 === count($this->filters)) { - return 0; - } - - return count(call_user_func_array('array_merge', array_values($this->filters))); - } - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - if (null === $this->sorted) { - if (0 === count($this->filters)) { - $this->sorted = $this->filters; - } else { - krsort($this->filters); - $this->sorted = call_user_func_array('array_merge', array_values($this->filters)); - } - } - - return new \ArrayIterator($this->sorted); - } -} diff --git a/src/FFMpeg/Filters/Frame/CustomFrameFilter.php b/src/FFMpeg/Filters/Frame/CustomFrameFilter.php deleted file mode 100644 index 92d3599..0000000 --- a/src/FFMpeg/Filters/Frame/CustomFrameFilter.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Frame; - -use FFMpeg\Media\Frame; - -class CustomFrameFilter implements FrameFilterInterface -{ - /** @var string */ - private $filter; - /** @var int */ - private $priority; - - /** - * A custom filter, useful if you want to build complex filters. - * - * @param string $filter - * @param int $priority - */ - public function __construct($filter, $priority = 0) - { - $this->filter = $filter; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Frame $frame) - { - $commands = ['-vf', $this->filter]; - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Frame/DisplayRatioFixerFilter.php b/src/FFMpeg/Filters/Frame/DisplayRatioFixerFilter.php deleted file mode 100644 index 7c2d9d0..0000000 --- a/src/FFMpeg/Filters/Frame/DisplayRatioFixerFilter.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Frame; - -use FFMpeg\Exception\RuntimeException; -use FFMpeg\Media\Frame; - -class DisplayRatioFixerFilter implements FrameFilterInterface -{ - /** @var int */ - private $priority; - - public function __construct($priority = 0) - { - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Frame $frame) - { - $dimensions = null; - $commands = []; - - foreach ($frame->getVideo()->getStreams() as $stream) { - if ($stream->isVideo()) { - try { - $dimensions = $stream->getDimensions(); - $commands[] = '-s'; - $commands[] = $dimensions->getWidth().'x'.$dimensions->getHeight(); - break; - } catch (RuntimeException $e) { - } - } - } - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Frame/FrameFilterInterface.php b/src/FFMpeg/Filters/Frame/FrameFilterInterface.php deleted file mode 100644 index d9df0e4..0000000 --- a/src/FFMpeg/Filters/Frame/FrameFilterInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Frame; - -use FFMpeg\Filters\FilterInterface; -use FFMpeg\Media\Frame; - -interface FrameFilterInterface extends FilterInterface -{ - public function apply(Frame $frame); -} diff --git a/src/FFMpeg/Filters/Frame/FrameFilters.php b/src/FFMpeg/Filters/Frame/FrameFilters.php deleted file mode 100644 index 8f2f45b..0000000 --- a/src/FFMpeg/Filters/Frame/FrameFilters.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Frame; - -use FFMpeg\Media\Frame; - -class FrameFilters -{ - private $frame; - - public function __construct(Frame $frame) - { - $this->frame = $frame; - } - - /** - * Fixes the display ratio of the output frame. - * - * In case the sample ratio and display ratio are different, image may be - * anamorphozed. This filter fixes this by specifying the output size. - * - * @return FrameFilters - */ - public function fixDisplayRatio() - { - $this->frame->addFilter(new DisplayRatioFixerFilter()); - - return $this; - } - - /** - * Applies a custom filter: -vf foo bar. - * - * @param string $parameters - * - * @return FrameFilters - */ - public function custom($parameters) - { - $this->frame->addFilter(new CustomFrameFilter($parameters)); - - return $this; - } -} diff --git a/src/FFMpeg/Filters/Gif/GifFilterInterface.php b/src/FFMpeg/Filters/Gif/GifFilterInterface.php deleted file mode 100644 index 141bbad..0000000 --- a/src/FFMpeg/Filters/Gif/GifFilterInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Gif; - -use FFMpeg\Filters\FilterInterface; -use FFMpeg\Media\Gif; - -interface GifFilterInterface extends FilterInterface -{ - public function apply(Gif $gif); -} diff --git a/src/FFMpeg/Filters/Gif/GifFilters.php b/src/FFMpeg/Filters/Gif/GifFilters.php deleted file mode 100644 index 9834c91..0000000 --- a/src/FFMpeg/Filters/Gif/GifFilters.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Gif; - -use FFMpeg\Media\Gif; - -class GifFilters -{ - private $gif; - - public function __construct(Gif $gif) - { - $this->gif = $gif; - } -} diff --git a/src/FFMpeg/Filters/Video/ClipFilter.php b/src/FFMpeg/Filters/Video/ClipFilter.php deleted file mode 100644 index 320996c..0000000 --- a/src/FFMpeg/Filters/Video/ClipFilter.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * 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\TimeCode; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\Video; - -class ClipFilter implements VideoFilterInterface -{ - /** @var TimeCode */ - private $start; - /** @var TimeCode */ - private $duration; - /** @var int */ - private $priority; - - public function __construct(TimeCode $start, TimeCode $duration = null, $priority = 0) - { - $this->start = $start; - $this->duration = $duration; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * @return TimeCode - */ - public function getStart() - { - return $this->start; - } - - /** - * @return TimeCode - */ - public function getDuration() - { - return $this->duration; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - $commands = ['-ss', (string) $this->start]; - - if (null !== $this->duration) { - $commands[] = '-t'; - $commands[] = (string) $this->duration; - } - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Video/CropFilter.php b/src/FFMpeg/Filters/Video/CropFilter.php deleted file mode 100644 index 459c854..0000000 --- a/src/FFMpeg/Filters/Video/CropFilter.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * 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\Coordinate\Point; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\Video; - -class CropFilter implements VideoFilterInterface -{ - /** @var int */ - protected $priority; - /** @var Dimension */ - protected $dimension; - /** @var Point */ - protected $point; - - public function __construct(Point $point, Dimension $dimension, $priority = 0) - { - $this->priority = $priority; - $this->dimension = $dimension; - $this->point = $point; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - foreach ($video->getStreams()->videos() as $stream) { - if ($stream->has('width') && $stream->has('height')) { - $stream->set('width', $this->dimension->getWidth()); - $stream->set('height', $this->dimension->getHeight()); - } - } - - return [ - '-filter:v', - 'crop='. - $this->dimension->getWidth().':'.$this->dimension->getHeight().':'.$this->point->getX().':'.$this->point->getY(), - ]; - } -} diff --git a/src/FFMpeg/Filters/Video/CustomFilter.php b/src/FFMpeg/Filters/Video/CustomFilter.php deleted file mode 100644 index ecfe2ae..0000000 --- a/src/FFMpeg/Filters/Video/CustomFilter.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * 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\Format\VideoInterface; -use FFMpeg\Media\Video; - -class CustomFilter implements VideoFilterInterface -{ - /** @var string */ - private $filter; - /** @var int */ - private $priority; - - /** - * A custom filter, useful if you want to build complex filters. - * - * @param string $filter - * @param int $priority - */ - public function __construct($filter, $priority = 0) - { - $this->filter = $filter; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - $commands = ['-vf', $this->filter]; - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Video/ExtractMultipleFramesFilter.php b/src/FFMpeg/Filters/Video/ExtractMultipleFramesFilter.php deleted file mode 100644 index 3f13806..0000000 --- a/src/FFMpeg/Filters/Video/ExtractMultipleFramesFilter.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * 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\Format\VideoInterface; -use FFMpeg\Media\Video; - -class ExtractMultipleFramesFilter implements VideoFilterInterface -{ - /** will extract a frame every second */ - public const FRAMERATE_EVERY_SEC = '1/1'; - /** will extract a frame every 2 seconds */ - public const FRAMERATE_EVERY_2SEC = '1/2'; - /** will extract a frame every 5 seconds */ - public const FRAMERATE_EVERY_5SEC = '1/5'; - /** will extract a frame every 10 seconds */ - public const FRAMERATE_EVERY_10SEC = '1/10'; - /** will extract a frame every 30 seconds */ - public const FRAMERATE_EVERY_30SEC = '1/30'; - /** will extract a frame every minute */ - public const FRAMERATE_EVERY_60SEC = '1/60'; - - /** @var int */ - private $priority; - private $frameRate; - private $destinationFolder; - private $frameFileType = 'jpg'; - - /** @var array */ - private static $supportedFrameFileTypes = array('jpg', 'jpeg', 'png'); - - 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 (0 != strcmp(substr($destinationFolder, -1), '/')) { - $destinationFolder .= '/'; - } - - // Set the destination folder - $this->destinationFolder = $destinationFolder; - } - - /** - * @param string $frameFileType - * - * @throws \FFMpeg\Exception\InvalidArgumentException - * - * @return ExtractMultipleFramesFilter - */ - public function setFrameFileType($frameFileType) - { - if (in_array($frameFileType, self::$supportedFrameFileTypes)) { - $this->frameFileType = $frameFileType; - - return $this; - } - - throw new InvalidArgumentException('Invalid frame file type, use: '.implode(',', self::$supportedFrameFileTypes)); - } - - /** - * {@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 = []; - $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 (false !== preg_match('/(\d+)(?:\s*)([\+\-\*\/])(?:\s*)(\d+)/', $this->frameRate, $matches)) { - $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.'.$this->frameFileType; - } catch (RuntimeException $e) { - throw new RuntimeException('An error occured while extracting the frames: '.$e->getMessage().'. The code: '.$e->getCode()); - } - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Video/FrameRateFilter.php b/src/FFMpeg/Filters/Video/FrameRateFilter.php deleted file mode 100644 index 47caaff..0000000 --- a/src/FFMpeg/Filters/Video/FrameRateFilter.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * 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\FrameRate; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\Video; - -class FrameRateFilter implements VideoFilterInterface -{ - private $rate; - private $gop; - private $priority; - - public function __construct(FrameRate $rate, $gop, $priority = 0) - { - $this->rate = $rate; - $this->gop = $gop; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * Returns the frame rate. - * - * @return FrameRate - */ - public function getFrameRate() - { - return $this->rate; - } - - /** - * Returns the GOP size. - * - * @see https://wikipedia.org/wiki/Group_of_pictures - * - * @return int - */ - public function getGOP() - { - return $this->gop; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - $commands = ['-r', $this->rate->getValue()]; - - /* - * @see http://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping - */ - if ($format->supportBFrames()) { - $commands[] = '-b_strategy'; - $commands[] = '1'; - $commands[] = '-bf'; - $commands[] = '3'; - $commands[] = '-g'; - $commands[] = $this->gop; - } - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Video/PadFilter.php b/src/FFMpeg/Filters/Video/PadFilter.php deleted file mode 100644 index dcb130c..0000000 --- a/src/FFMpeg/Filters/Video/PadFilter.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * 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\Filters\AdvancedMedia\ComplexCompatibleFilter; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\AdvancedMedia; -use FFMpeg\Media\Video; - -class PadFilter implements VideoFilterInterface, ComplexCompatibleFilter -{ - /** @var Dimension */ - private $dimension; - /** @var int */ - private $priority; - - public function __construct(Dimension $dimension, $priority = 0) - { - $this->dimension = $dimension; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * @return Dimension - */ - public function getDimension() - { - return $this->dimension; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return 'pad'; - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - return '0.4.9'; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - return $this->getCommands(); - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return $this->getCommands(); - } - - /** - * @return array - */ - protected function getCommands() - { - $commands = []; - - $commands[] = '-vf'; - $commands[] = 'scale=iw*min('.$this->dimension->getWidth().'/iw\,'.$this->dimension->getHeight() - .'/ih):ih*min('.$this->dimension->getWidth().'/iw\,'.$this->dimension->getHeight().'/ih),pad=' - .$this->dimension->getWidth().':'.$this->dimension->getHeight().':('.$this->dimension->getWidth() - .'-iw)/2:('.$this->dimension->getHeight().'-ih)/2'; - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Video/ResizeFilter.php b/src/FFMpeg/Filters/Video/ResizeFilter.php deleted file mode 100644 index 54d2cf5..0000000 --- a/src/FFMpeg/Filters/Video/ResizeFilter.php +++ /dev/null @@ -1,141 +0,0 @@ - - * - * 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\Format\VideoInterface; -use FFMpeg\Media\Video; - -class ResizeFilter implements VideoFilterInterface -{ - /** fits to the dimensions, might introduce anamorphosis */ - public const RESIZEMODE_FIT = 'fit'; - /** resizes the video inside the given dimension, no anamorphosis */ - public const RESIZEMODE_INSET = 'inset'; - /** resizes the video to fit the dimension width, no anamorphosis */ - public const RESIZEMODE_SCALE_WIDTH = 'width'; - /** resizes the video to fit the dimension height, no anamorphosis */ - public const RESIZEMODE_SCALE_HEIGHT = 'height'; - - /** @var Dimension */ - private $dimension; - /** @var string */ - private $mode; - /** @var bool */ - private $forceStandards; - /** @var int */ - 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 bool - */ - public function areStandardsForced() - { - return $this->forceStandards; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - $dimensions = null; - $commands = []; - - 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); - } -} diff --git a/src/FFMpeg/Filters/Video/RotateFilter.php b/src/FFMpeg/Filters/Video/RotateFilter.php deleted file mode 100644 index 497caa4..0000000 --- a/src/FFMpeg/Filters/Video/RotateFilter.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * 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\Format\VideoInterface; -use FFMpeg\Media\Video; - -class RotateFilter implements VideoFilterInterface -{ - public const ROTATE_90 = 'transpose=1'; - public const ROTATE_180 = 'hflip,vflip'; - public const ROTATE_270 = 'transpose=2'; - - /** @var string */ - private $angle; - /** @var int */ - 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, [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 ['-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.'); - } - } -} diff --git a/src/FFMpeg/Filters/Video/SynchronizeFilter.php b/src/FFMpeg/Filters/Video/SynchronizeFilter.php deleted file mode 100644 index c9c2473..0000000 --- a/src/FFMpeg/Filters/Video/SynchronizeFilter.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * 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\Format\VideoInterface; -use FFMpeg\Media\Video; - -/** - * Synchronizes audio and video in case of desynchronized movies. - */ -class SynchronizeFilter implements VideoFilterInterface -{ - private $priority; - - public function __construct($priority = 12) - { - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - return ['-async', '1', '-metadata:s:v:0', 'start_time=0']; - } -} diff --git a/src/FFMpeg/Filters/Video/VideoFilterInterface.php b/src/FFMpeg/Filters/Video/VideoFilterInterface.php deleted file mode 100644 index 192cf37..0000000 --- a/src/FFMpeg/Filters/Video/VideoFilterInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * 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\Filters\FilterInterface; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\Video; - -interface VideoFilterInterface extends FilterInterface -{ - /** - * Applies the filter on the the Video media given an format. - * - * @return array An array of arguments - */ - public function apply(Video $video, VideoInterface $format); -} diff --git a/src/FFMpeg/Filters/Video/VideoFilters.php b/src/FFMpeg/Filters/Video/VideoFilters.php deleted file mode 100644 index 2c724de..0000000 --- a/src/FFMpeg/Filters/Video/VideoFilters.php +++ /dev/null @@ -1,170 +0,0 @@ - - * - * 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\Coordinate\FrameRate; -use FFMpeg\Coordinate\Point; -use FFMpeg\Coordinate\TimeCode; -use FFMpeg\Filters\Audio\AudioFilters; -use FFMpeg\Filters\Audio\AudioResamplableFilter; -use FFMpeg\Media\Video; - -class VideoFilters extends AudioFilters -{ - public function __construct(Video $media) - { - parent::__construct($media); - } - - /** - * Resizes a video to a given dimension. - * - * @param string $mode - * @param bool $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 int $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 int $rate - * - * @return AudioFilters - */ - public function audioResample($rate) - { - $this->media->addFilter(new AudioResamplableFilter($rate)); - - return $this; - } - - /** - * Adds padding (black bars) to a video. - * - * @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. - * - * @return VideoFilters - */ - public function crop(Point $point, Dimension $dimension) - { - $this->media->addFilter(new CropFilter($point, $dimension)); - - return $this; - } - - /** - * @param string $imagePath - * - * @return $this - */ - public function watermark($imagePath, array $coordinates = []) - { - $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; - } -} diff --git a/src/FFMpeg/Filters/Video/WatermarkFilter.php b/src/FFMpeg/Filters/Video/WatermarkFilter.php deleted file mode 100644 index e70cffb..0000000 --- a/src/FFMpeg/Filters/Video/WatermarkFilter.php +++ /dev/null @@ -1,121 +0,0 @@ - - * - * 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\Filters\AdvancedMedia\ComplexCompatibleFilter; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\AdvancedMedia; -use FFMpeg\Media\Video; - -class WatermarkFilter implements VideoFilterInterface, ComplexCompatibleFilter -{ - /** @var string */ - private $watermarkPath; - /** @var array */ - private $coordinates; - /** @var int */ - private $priority; - - public function __construct($watermarkPath, array $coordinates = [], $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; - } - - /** - * Get name of the filter. - * - * @return string - */ - public function getName() - { - return 'watermark'; - } - - /** - * Get minimal version of ffmpeg starting with which this filter is supported. - * - * @return string - */ - public function getMinimalFFMpegVersion() - { - return '0.8'; - } - - /** - * {@inheritdoc} - */ - public function apply(Video $video, VideoInterface $format) - { - return $this->getCommands(); - } - - /** - * {@inheritdoc} - */ - public function applyComplex(AdvancedMedia $media) - { - return $this->getCommands(); - } - - /** - * @return array - */ - protected function getCommands() - { - $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 = 'main_h - '.$this->coordinates['bottom'].' - overlay_h'; - } else { - $y = 0; - } - - if (isset($this->coordinates['left'])) { - $x = $this->coordinates['left']; - } elseif (isset($this->coordinates['right'])) { - $x = 'main_w - '.$this->coordinates['right'].' - overlay_w'; - } 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 [ - '-vf', - 'movie='.$this->watermarkPath.' [watermark]; [in][watermark] overlay='.$x.':'.$y.' [out]', - ]; - } -} diff --git a/src/FFMpeg/Filters/Waveform/WaveformDownmixFilter.php b/src/FFMpeg/Filters/Waveform/WaveformDownmixFilter.php deleted file mode 100644 index d6e5186..0000000 --- a/src/FFMpeg/Filters/Waveform/WaveformDownmixFilter.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Waveform; - -use FFMpeg\Exception\RuntimeException; -use FFMpeg\Media\Waveform; - -class WaveformDownmixFilter implements WaveformFilterInterface -{ - /** @var bool */ - private $downmix; - /** @var int */ - private $priority; - - // By default, the downmix value is set to FALSE. - public function __construct($downmix = false, $priority = 0) - { - $this->downmix = $downmix; - $this->priority = $priority; - } - - /** - * {@inheritdoc} - */ - public function getDownmix() - { - return $this->downmix; - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return $this->priority; - } - - /** - * {@inheritdoc} - */ - public function apply(Waveform $waveform) - { - $commands = []; - - foreach ($waveform->getAudio()->getStreams() as $stream) { - if ($stream->isAudio()) { - try { - // If the downmix parameter is set to TRUE, we add an option to the FFMPEG command - if (true == $this->downmix) { - $commands[] = '"aformat=channel_layouts=mono"'; - } - - break; - } catch (RuntimeException $e) { - } - } - } - - return $commands; - } -} diff --git a/src/FFMpeg/Filters/Waveform/WaveformFilterInterface.php b/src/FFMpeg/Filters/Waveform/WaveformFilterInterface.php deleted file mode 100644 index 51e69bc..0000000 --- a/src/FFMpeg/Filters/Waveform/WaveformFilterInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Waveform; - -use FFMpeg\Filters\FilterInterface; -use FFMpeg\Media\Waveform; - -interface WaveformFilterInterface extends FilterInterface -{ - public function apply(Waveform $waveform); -} diff --git a/src/FFMpeg/Filters/Waveform/WaveformFilters.php b/src/FFMpeg/Filters/Waveform/WaveformFilters.php deleted file mode 100644 index 6be9068..0000000 --- a/src/FFMpeg/Filters/Waveform/WaveformFilters.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Filters\Waveform; - -use FFMpeg\Media\Waveform; - -class WaveformFilters -{ - private $waveform; - - public function __construct(Waveform $waveform) - { - $this->waveform = $waveform; - } - - /** - * Sets the downmix of the output waveform. - * - * If you want a simpler waveform, sets the downmix to TRUE. - * - * @return WaveformFilters - */ - public function setDownmix() - { - $this->waveform->addFilter(new WaveformDownmixFilter()); - - return $this; - } -} diff --git a/src/FFMpeg/Format/Audio/Aac.php b/src/FFMpeg/Format/Audio/Aac.php deleted file mode 100644 index 9d21308..0000000 --- a/src/FFMpeg/Format/Audio/Aac.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Audio; - -/** - * The AAC audio format. - */ -class Aac extends DefaultAudio -{ - public function __construct() - { - $this->audioCodec = 'libfdk_aac'; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['libfdk_aac']; - } -} diff --git a/src/FFMpeg/Format/Audio/DefaultAudio.php b/src/FFMpeg/Format/Audio/DefaultAudio.php deleted file mode 100644 index cc41594..0000000 --- a/src/FFMpeg/Format/Audio/DefaultAudio.php +++ /dev/null @@ -1,141 +0,0 @@ - - * - * 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\FFProbe; -use FFMpeg\Format\AudioInterface; -use FFMpeg\Format\ProgressableInterface; -use FFMpeg\Format\ProgressListener\AudioProgressListener; -use FFMpeg\Media\MediaTypeInterface; - -abstract class DefaultAudio extends EventEmitter implements AudioInterface, ProgressableInterface -{ - /** @var string */ - protected $audioCodec; - - /** @var int */ - protected $audioKiloBitrate = 128; - - /** @var int */ - protected $audioChannels = null; - - /** - * {@inheritdoc} - */ - public function getExtraParams() - { - return []; - } - - /** - * {@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 int $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 int $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, $duration = 0) - { - $format = $this; - $listener = new AudioProgressListener($ffprobe, $media->getPathfile(), $pass, $total, $duration); - $listener->on('progress', function () use ($media, $format) { - $format->emit('progress', array_merge([$media, $format], func_get_args())); - }); - - return [$listener]; - } - - /** - * {@inheritDoc} - */ - public function getPasses() - { - return 1; - } -} diff --git a/src/FFMpeg/Format/Audio/Flac.php b/src/FFMpeg/Format/Audio/Flac.php deleted file mode 100644 index a2faccb..0000000 --- a/src/FFMpeg/Format/Audio/Flac.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Audio; - -/** - * The Flac audio format. - */ -class Flac extends DefaultAudio -{ - public function __construct() - { - $this->audioCodec = 'flac'; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['flac']; - } -} diff --git a/src/FFMpeg/Format/Audio/Mp3.php b/src/FFMpeg/Format/Audio/Mp3.php deleted file mode 100644 index bc5c373..0000000 --- a/src/FFMpeg/Format/Audio/Mp3.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Audio; - -/** - * The MP3 audio format. - */ -class Mp3 extends DefaultAudio -{ - public function __construct() - { - $this->audioCodec = 'libmp3lame'; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['libmp3lame']; - } -} diff --git a/src/FFMpeg/Format/Audio/Vorbis.php b/src/FFMpeg/Format/Audio/Vorbis.php deleted file mode 100644 index 4cb1def..0000000 --- a/src/FFMpeg/Format/Audio/Vorbis.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Audio; - -/** - * The Vorbis audio format. - */ -class Vorbis extends DefaultAudio -{ - public function __construct() - { - $this->audioCodec = 'vorbis'; - } - - /** - * {@inheritdoc} - */ - public function getExtraParams() - { - return ['-strict', '-2']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['vorbis']; - } -} diff --git a/src/FFMpeg/Format/Audio/Wav.php b/src/FFMpeg/Format/Audio/Wav.php deleted file mode 100644 index 04a444c..0000000 --- a/src/FFMpeg/Format/Audio/Wav.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Audio; - -/** - * The WAV audio format. - */ -class Wav extends DefaultAudio -{ - public function __construct() - { - $this->audioCodec = 'pcm_s16le'; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['pcm_s16le']; - } -} diff --git a/src/FFMpeg/Format/AudioInterface.php b/src/FFMpeg/Format/AudioInterface.php deleted file mode 100644 index 4aeaa54..0000000 --- a/src/FFMpeg/Format/AudioInterface.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format; - -interface AudioInterface extends FormatInterface -{ - /** - * Gets the audio kiloBitrate value. - * - * @return int - */ - public function getAudioKiloBitrate(); - - /** - * Gets the audio channels value. - * - * @return int - */ - public function getAudioChannels(); - - /** - * Returns the audio codec. - * - * @return string - */ - public function getAudioCodec(); - - /** - * Returns the list of available audio codecs for this format. - * - * @return array - */ - public function getAvailableAudioCodecs(); -} diff --git a/src/FFMpeg/Format/FormatInterface.php b/src/FFMpeg/Format/FormatInterface.php deleted file mode 100644 index 86a341b..0000000 --- a/src/FFMpeg/Format/FormatInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format; - -interface FormatInterface -{ - /** - * Returns the number of passes. - * - * @return string - */ - public function getPasses(); - - /** - * Returns an array of extra parameters to add to ffmpeg commandline. - * - * @return array - */ - public function getExtraParams(); -} diff --git a/src/FFMpeg/Format/FrameInterface.php b/src/FFMpeg/Format/FrameInterface.php deleted file mode 100644 index 77a27ab..0000000 --- a/src/FFMpeg/Format/FrameInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format; - -interface FrameInterface extends FormatInterface -{ -} diff --git a/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php b/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php deleted file mode 100644 index c149045..0000000 --- a/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php +++ /dev/null @@ -1,260 +0,0 @@ - - * - * 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\Exception\RuntimeException; -use FFMpeg\FFProbe; - -/** - * @author Robert Gruendler - */ -abstract class AbstractProgressListener extends EventEmitter implements ListenerInterface -{ - /** @var int */ - private $duration; - - /** @var int */ - private $totalSize; - - /** @var int */ - private $currentSize; - - /** @var int */ - private $currentTime; - - /** @var float */ - private $lastOutput = null; - - /** @var FFProbe */ - private $ffprobe; - - /** @var string */ - private $pathfile; - - /** @var bool */ - private $initialized = false; - - /** @var int */ - private $currentPass; - - /** @var int */ - private $totalPass; - - /** - * Transcoding rate in kb/s. - * - * @var int - */ - private $rate; - - /** - * Percentage of transcoding progress (0 - 100). - * - * @var int - */ - private $percent = 0; - - /** - * Time remaining (seconds). - * - * @var int - */ - private $remaining = null; - - /** - * @param string $pathfile - * @param int $currentPass The current pass number - * @param int $totalPass The total number of passes - * @param int $duration - */ - public function __construct(FFProbe $ffprobe, $pathfile, $currentPass, $totalPass, $duration = 0) - { - $this->ffprobe = $ffprobe; - $this->pathfile = $pathfile; - $this->currentPass = $currentPass; - $this->totalPass = $totalPass; - $this->duration = $duration; - } - - /** - * @return FFProbe - */ - public function getFFProbe() - { - return $this->ffprobe; - } - - /** - * @return string - */ - public function getPathfile() - { - return $this->pathfile; - } - - /** - * @return int - */ - public function getCurrentPass() - { - return $this->currentPass; - } - - /** - * @return int - */ - 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 []; - } - - /** - * 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 = []; - - if (1 !== preg_match($this->getPattern(), $progress, $matches)) { - 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 (null !== $this->lastOutput) { - $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 (null === $this->remaining) { - return null; - } - - return [ - '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->duration = (int) $this->duration > 0 ? $this->duration : $format->get('duration'); - $this->totalSize = $format->get('size') / 1024 * ($this->duration / $format->get('duration')); - $this->initialized = true; - } -} diff --git a/src/FFMpeg/Format/ProgressListener/AudioProgressListener.php b/src/FFMpeg/Format/ProgressListener/AudioProgressListener.php deleted file mode 100644 index 77ecf46..0000000 --- a/src/FFMpeg/Format/ProgressListener/AudioProgressListener.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\ProgressListener; - -/** - * Parses ffmpeg stderr progress information. An example:. - * - *
- *       size=    3552kB time=00:03:47.29 bitrate= 128.0kbits/s
- * 
- * - * @author Robert Gruendler - */ -class AudioProgressListener extends AbstractProgressListener -{ - public function getPattern() - { - return '/size=(.*?) time=(.*?) /'; - } -} diff --git a/src/FFMpeg/Format/ProgressListener/VideoProgressListener.php b/src/FFMpeg/Format/ProgressListener/VideoProgressListener.php deleted file mode 100644 index b26c4bb..0000000 --- a/src/FFMpeg/Format/ProgressListener/VideoProgressListener.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\ProgressListener; - -/** - * Parses ffmpeg stderr progress information for video files. An example:. - * - *
- *       frame=  171 fps=0.0 q=10.0 size=      18kB time=00:00:05.72 bitrate=  26.4kbits/s dup=8 drop=0
- * 
- * - * @author Robert Gruendler - */ -class VideoProgressListener extends AbstractProgressListener -{ - public function getPattern() - { - return '/size=(.*?) time=(.*?) /'; - } -} diff --git a/src/FFMpeg/Format/ProgressableInterface.php b/src/FFMpeg/Format/ProgressableInterface.php deleted file mode 100644 index 9412fec..0000000 --- a/src/FFMpeg/Format/ProgressableInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format; - -use Evenement\EventEmitterInterface; -use FFMpeg\FFProbe; -use FFMpeg\Media\MediaTypeInterface; - -interface ProgressableInterface extends EventEmitterInterface -{ - /** - * Creates the progress listener. - * - * @param int $pass The current pas snumber - * @param int $total The total pass number - * @param int $duration The new video duration - * - * @return array An array of listeners - */ - public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total, $duration = 0); -} diff --git a/src/FFMpeg/Format/Video/DefaultVideo.php b/src/FFMpeg/Format/Video/DefaultVideo.php deleted file mode 100644 index e9a32e7..0000000 --- a/src/FFMpeg/Format/Video/DefaultVideo.php +++ /dev/null @@ -1,170 +0,0 @@ - - * - * 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\Exception\InvalidArgumentException; -use FFMpeg\FFProbe; -use FFMpeg\Format\Audio\DefaultAudio; -use FFMpeg\Format\ProgressListener\VideoProgressListener; -use FFMpeg\Format\VideoInterface; -use FFMpeg\Media\MediaTypeInterface; - -/** - * The abstract default Video format. - */ -abstract class DefaultVideo extends DefaultAudio implements VideoInterface -{ - /** @var string */ - protected $videoCodec; - - /** @var int */ - protected $kiloBitrate = 1000; - - /** @var int */ - protected $modulus = 16; - - /** @var array */ - protected $additionalParamaters; - - /** @var array */ - protected $initialParamaters; - - /** - * {@inheritdoc} - */ - public function getKiloBitrate() - { - return $this->kiloBitrate; - } - - /** - * Sets the kiloBitrate value. - * - * @param int $kiloBitrate - * - * @throws InvalidArgumentException - */ - public function setKiloBitrate($kiloBitrate) - { - if ($kiloBitrate < 0) { - 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 int - */ - 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 getInitialParameters() - { - return $this->initialParamaters; - } - - /** - * Sets initial parameters. - * - * @param array $initialParamaters - * - * @throws InvalidArgumentException - */ - public function setInitialParameters($initialParamaters) - { - if (!is_array($initialParamaters)) { - throw new InvalidArgumentException('Wrong initialParamaters value'); - } - - $this->initialParamaters = $initialParamaters; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total, $duration = 0) - { - $format = $this; - $listeners = [new VideoProgressListener($ffprobe, $media->getPathfile(), $pass, $total, $duration)]; - - foreach ($listeners as $listener) { - $listener->on('progress', function () use ($format, $media) { - $format->emit('progress', array_merge([$media, $format], func_get_args())); - }); - } - - return $listeners; - } -} diff --git a/src/FFMpeg/Format/Video/Ogg.php b/src/FFMpeg/Format/Video/Ogg.php deleted file mode 100644 index 62861bb..0000000 --- a/src/FFMpeg/Format/Video/Ogg.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Video; - -/** - * The Ogg video format. - */ -class Ogg extends DefaultVideo -{ - public function __construct($audioCodec = 'libvorbis', $videoCodec = 'libtheora') - { - $this - ->setAudioCodec($audioCodec) - ->setVideoCodec($videoCodec); - } - - /** - * {@inheritDoc} - */ - public function supportBFrames() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['libvorbis']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableVideoCodecs() - { - return ['libtheora']; - } -} diff --git a/src/FFMpeg/Format/Video/WMV.php b/src/FFMpeg/Format/Video/WMV.php deleted file mode 100644 index ea803e5..0000000 --- a/src/FFMpeg/Format/Video/WMV.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Video; - -/** - * The WMV video format. - */ -class WMV extends DefaultVideo -{ - public function __construct($audioCodec = 'wmav2', $videoCodec = 'wmv2') - { - $this - ->setAudioCodec($audioCodec) - ->setVideoCodec($videoCodec); - } - - /** - * {@inheritDoc} - */ - public function supportBFrames() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['wmav2']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableVideoCodecs() - { - return ['wmv2']; - } -} diff --git a/src/FFMpeg/Format/Video/WMV3.php b/src/FFMpeg/Format/Video/WMV3.php deleted file mode 100644 index 2ddf225..0000000 --- a/src/FFMpeg/Format/Video/WMV3.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Video; - -/** - * The WMV video format. - */ -class WMV3 extends DefaultVideo -{ - public function __construct($audioCodec = 'wmav3', $videoCodec = 'wmv3') - { - $this - ->setAudioCodec($audioCodec) - ->setVideoCodec($videoCodec); - } - - /** - * {@inheritDoc} - */ - public function supportBFrames() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['wmav3']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableVideoCodecs() - { - return ['wmv3']; - } -} diff --git a/src/FFMpeg/Format/Video/WebM.php b/src/FFMpeg/Format/Video/WebM.php deleted file mode 100644 index 8d75f18..0000000 --- a/src/FFMpeg/Format/Video/WebM.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Video; - -/** - * The WebM video format. - */ -class WebM extends DefaultVideo -{ - public function __construct($audioCodec = 'libvorbis', $videoCodec = 'libvpx') - { - $this - ->setAudioCodec($audioCodec) - ->setVideoCodec($videoCodec); - } - - /** - * {@inheritDoc} - */ - public function supportBFrames() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getExtraParams() - { - return ['-f', 'webm']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['copy', 'libvorbis']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableVideoCodecs() - { - return ['libvpx', 'libvpx-vp9']; - } -} diff --git a/src/FFMpeg/Format/Video/X264.php b/src/FFMpeg/Format/Video/X264.php deleted file mode 100644 index 668637a..0000000 --- a/src/FFMpeg/Format/Video/X264.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format\Video; - -/** - * The X264 video format. - */ -class X264 extends DefaultVideo -{ - /** @var bool */ - private $bframesSupport = true; - - /** @var int */ - private $passes = 2; - - public function __construct($audioCodec = 'aac', $videoCodec = 'libx264') - { - $this - ->setAudioCodec($audioCodec) - ->setVideoCodec($videoCodec); - } - - /** - * {@inheritDoc} - */ - public function supportBFrames() - { - return $this->bframesSupport; - } - - /** - * @param $support - * - * @return X264 - */ - public function setBFramesSupport($support) - { - $this->bframesSupport = $support; - - return $this; - } - - /** - * {@inheritDoc} - */ - public function getAvailableAudioCodecs() - { - return ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac']; - } - - /** - * {@inheritDoc} - */ - public function getAvailableVideoCodecs() - { - return ['libx264']; - } - - /** - * @param $passes - * - * @return X264 - */ - public function setPasses($passes) - { - $this->passes = $passes; - - return $this; - } - - /** - * {@inheritDoc} - */ - public function getPasses() - { - return 0 === $this->getKiloBitrate() ? 1 : $this->passes; - } - - /** - * @return int - */ - public function getModulus() - { - return 2; - } -} diff --git a/src/FFMpeg/Format/VideoInterface.php b/src/FFMpeg/Format/VideoInterface.php deleted file mode 100644 index 83a44cd..0000000 --- a/src/FFMpeg/Format/VideoInterface.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Format; - -interface VideoInterface extends AudioInterface -{ - /** - * Gets the kiloBitrate value. - * - * @return int - */ - public function getKiloBitrate(); - - /** - * Returns the modulus used by the Resizable video. - * - * This used to calculate the target dimensions while maintaining the best - * aspect ratio. - * - * @see http://www.undeadborn.net/tools/rescalculator.php - * - * @return int - */ - public function getModulus(); - - /** - * Returns the video codec. - * - * @return string - */ - public function getVideoCodec(); - - /** - * Returns true if the current format supports B-Frames. - * - * @see https://wikipedia.org/wiki/Video_compression_picture_types - * - * @return bool - */ - public function supportBFrames(); - - /** - * Returns the list of available video codecs for this format. - * - * @return array - */ - public function getAvailableVideoCodecs(); - - /** - * Returns the list of additional parameters for this format. - * - * @return array - */ - public function getAdditionalParameters(); - - /** - * Returns the list of initial parameters for this format. - * - * @return array - */ - public function getInitialParameters(); -} diff --git a/src/FFMpeg/Media/AbstractMediaType.php b/src/FFMpeg/Media/AbstractMediaType.php deleted file mode 100644 index 3a7aa50..0000000 --- a/src/FFMpeg/Media/AbstractMediaType.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * 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; -use Spatie\TemporaryDirectory\TemporaryDirectory; - -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; - } - - /** - * @return MediaTypeInterface - */ - public function setFFMpegDriver(FFMpegDriver $driver) - { - $this->driver = $driver; - - return $this; - } - - /** - * @return FFProbe - */ - public function getFFProbe() - { - return $this->ffprobe; - } - - /** - * @return MediaTypeInterface - */ - public function setFFProbe(FFProbe $ffprobe) - { - $this->ffprobe = $ffprobe; - - return $this; - } - - /** - * @return string - */ - public function getPathfile() - { - return $this->pathfile; - } - - /** - * @return MediaTypeInterface - */ - public function setFiltersCollection(FiltersCollection $filters) - { - $this->filters = $filters; - - return $this; - } - - /** - * @return MediaTypeInterface - */ - public function getFiltersCollection() - { - return $this->filters; - } - - /** - * Returns a new instance of TemporaryDirectory with the optionally configured directory. - * - * @return \Spatie\TemporaryDirectory\TemporaryDirectory - */ - public function getTemporaryDirectory(): TemporaryDirectory - { - return new TemporaryDirectory( - $this->driver->getConfiguration()->get('temporary_directory') ?: '' - ); - } - - protected function cleanupTemporaryFile($filename) - { - if (file_exists($filename) && is_writable($filename)) { - unlink($filename); - } - - return $this; - } -} diff --git a/src/FFMpeg/Media/AbstractStreamableMedia.php b/src/FFMpeg/Media/AbstractStreamableMedia.php deleted file mode 100644 index a28b277..0000000 --- a/src/FFMpeg/Media/AbstractStreamableMedia.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Media; - -use FFMpeg\FFProbe\DataMapping\Format; -use FFMpeg\FFProbe\DataMapping\StreamCollection; - -abstract class AbstractStreamableMedia extends AbstractMediaType -{ - private $streams; - - /** - * @return StreamCollection - */ - public function getStreams() - { - if (null === $this->streams) { - $this->streams = $this->ffprobe->streams($this->pathfile); - } - - return $this->streams; - } - - /** - * @return Format - */ - public function getFormat() - { - return $this->ffprobe->format($this->pathfile); - } -} diff --git a/src/FFMpeg/Media/AbstractVideo.php b/src/FFMpeg/Media/AbstractVideo.php deleted file mode 100644 index 93f21b9..0000000 --- a/src/FFMpeg/Media/AbstractVideo.php +++ /dev/null @@ -1,321 +0,0 @@ - - * - * 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\Exception\InvalidArgumentException; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\Filters\Audio\SimpleFilter; -use FFMpeg\Filters\FilterInterface; -use FFMpeg\Filters\Video\ClipFilter; -use FFMpeg\Filters\Video\VideoFilters; -use FFMpeg\Format\AudioInterface; -use FFMpeg\Format\FormatInterface; -use FFMpeg\Format\ProgressableInterface; -use FFMpeg\Format\VideoInterface; -use Spatie\TemporaryDirectory\TemporaryDirectory; - -abstract class AbstractVideo extends Audio -{ - /** - * FileSystem Manager instance. - * - * @var Manager - */ - protected $fs; - - /** - * FileSystem Manager ID. - * - * @var int - */ - protected $fsId; - - /** - * {@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 string $outputPathfile - * - * @return Video - * - * @throws RuntimeException - */ - public function save(FormatInterface $format, $outputPathfile) - { - $passes = $this->buildCommand($format, $outputPathfile); - - $failure = null; - $totalPasses = $format->getPasses(); - - foreach ($passes as $pass => $passCommands) { - try { - /** add listeners here */ - $listeners = null; - - if ($format instanceof ProgressableInterface) { - $filters = clone $this->filters; - $duration = 0; - - // check the filters of the video, and if the video has the ClipFilter then - // take the new video duration and send to the - // FFMpeg\Format\ProgressListener\AbstractProgressListener class - foreach ($filters as $filter) { - if ($filter instanceof ClipFilter) { - if (null === $filter->getDuration()) { - continue; - } - - $duration = $filter->getDuration()->toSeconds(); - break; - } - } - $listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses, $duration); - } - - $this->driver->command($passCommands, false, $listeners); - } catch (ExecutionFailureException $e) { - $failure = $e; - break; - } - } - - $this->fs->delete(); - - if (null !== $failure) { - throw new RuntimeException('Encoding failed', $failure->getCode(), $failure); - } - - return $this; - } - - /** - * NOTE: This method is different to the Audio's one, because Video is using passes. - * {@inheritDoc} - */ - public function getFinalCommand(FormatInterface $format, $outputPathfile) - { - $finalCommands = []; - - foreach ($this->buildCommand($format, $outputPathfile) as $pass => $passCommands) { - $finalCommands[] = implode(' ', $passCommands); - } - - $this->fs->delete(); - - return $finalCommands; - } - - /** - * **NOTE:** This creates passes instead of a single command! - * - * {@inheritDoc} - * - * @return string[][] - */ - protected function buildCommand(FormatInterface $format, $outputPathfile) - { - $commands = $this->basePartOfCommand($format); - - $filters = clone $this->filters; - $filters->add(new SimpleFilter($format->getExtraParams(), 10)); - - if ($this->driver->getConfiguration()->has('ffmpeg.threads')) { - $filters->add(new SimpleFilter(['-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')])); - } - if ($format instanceof VideoInterface) { - if (null !== $format->getVideoCodec()) { - $filters->add(new SimpleFilter(['-vcodec', $format->getVideoCodec()])); - } - } - if ($format instanceof AudioInterface) { - if (null !== $format->getAudioCodec()) { - $filters->add(new SimpleFilter(['-acodec', $format->getAudioCodec()])); - } - } - - foreach ($filters as $filter) { - $commands = array_merge($commands, $filter->apply($this, $format)); - } - - if ($format instanceof VideoInterface) { - if (0 !== $format->getKiloBitrate()) { - $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 ('-vf' === $command) { - $commandSplits = explode(';', $commands[$i + 1]); - if (1 == count($commandSplits)) { - $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; - } - - $this->fsId = uniqid('ffmpeg-passes'); - $this->fs = $this->getTemporaryDirectory()->name($this->fsId)->create(); - $passPrefix = $this->fs->path(uniqid('pass-')); - touch($passPrefix); - $passes = []; - $totalPasses = $format->getPasses(); - - if (!$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; - } - - return $passes; - } - - /** - * Return base part of command. - * - * @return array - */ - protected function basePartOfCommand(FormatInterface $format) - { - $commands = ['-y']; - - // If the user passed some initial parameters - if ($format instanceof VideoInterface) { - if (null !== $format->getInitialParameters()) { - foreach ($format->getInitialParameters() as $initialParameter) { - $commands[] = $initialParameter; - } - } - } - - $commands[] = '-i'; - $commands[] = $this->pathfile; - - return $commands; - } -} diff --git a/src/FFMpeg/Media/Audio.php b/src/FFMpeg/Media/Audio.php deleted file mode 100644 index bd624d0..0000000 --- a/src/FFMpeg/Media/Audio.php +++ /dev/null @@ -1,160 +0,0 @@ - - * - * 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\Exception\InvalidArgumentException; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\Filters\Audio\AudioFilterInterface; -use FFMpeg\Filters\Audio\AudioFilters; -use FFMpeg\Filters\Audio\SimpleFilter; -use FFMpeg\Filters\FilterInterface; -use FFMpeg\Format\FormatInterface; -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 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, 0); - } - - $commands = $this->buildCommand($format, $outputPathfile); - - try { - $this->driver->command($commands, false, $listeners); - } catch (ExecutionFailureException $e) { - $this->cleanupTemporaryFile($outputPathfile); - throw new RuntimeException('Encoding failed', $e->getCode(), $e); - } - - return $this; - } - - /** - * Returns the final command as a string, useful for debugging purposes. - * - * @param string $outputPathfile - * - * @return string - * - * @since 0.11.0 - */ - public function getFinalCommand(FormatInterface $format, $outputPathfile) - { - return implode(' ', $this->buildCommand($format, $outputPathfile)); - } - - /** - * Builds the command which will be executed with the provided format. - * - * @param string $outputPathfile - * - * @return string[] An array which are the components of the command - * - * @since 0.11.0 - */ - protected function buildCommand(FormatInterface $format, $outputPathfile) - { - $commands = ['-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(['-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')])); - } - if (null !== $format->getAudioCodec()) { - $filters->add(new SimpleFilter(['-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; - - return $commands; - } - - /** - * Gets the waveform of the video. - * - * @param int $width - * @param int $height - * @param array $colors Array of colors for ffmpeg to use. Color format is #000000 (RGB hex string with #) - * - * @return Waveform - */ - public function waveform($width = 640, $height = 120, $colors = [Waveform::DEFAULT_COLOR]) - { - return new Waveform($this, $this->driver, $this->ffprobe, $width, $height, $colors); - } - - /** - * Concatenates a list of audio files into one unique audio file. - * - * @param array $sources - * - * @return Concat - */ - public function concat($sources) - { - return new Concat($sources, $this->driver, $this->ffprobe); - } -} diff --git a/src/FFMpeg/Media/Clip.php b/src/FFMpeg/Media/Clip.php deleted file mode 100644 index 0d85b39..0000000 --- a/src/FFMpeg/Media/Clip.php +++ /dev/null @@ -1,61 +0,0 @@ -start = $start; - $this->duration = $duration; - $this->video = $video; - - parent::__construct($video->getPathfile(), $driver, $ffprobe); - } - - /** - * Returns the video related to the frame. - * - * @return Video - */ - public function getVideo() - { - return $this->video; - } - - /** - * Return base part of command. - * - * @return array - */ - protected function basePartOfCommand(FormatInterface $format) - { - $arr = ['-y', '-ss', (string) $this->start, '-i', $this->pathfile]; - - if (false === is_null($this->duration)) { - $arr[] = '-t'; - $arr[] = (string) $this->duration; - } - - return $arr; - } -} diff --git a/src/FFMpeg/Media/Concat.php b/src/FFMpeg/Media/Concat.php deleted file mode 100644 index 7ef0cf4..0000000 --- a/src/FFMpeg/Media/Concat.php +++ /dev/null @@ -1,261 +0,0 @@ - - * - * 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\Driver\FFMpegDriver; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\FFProbe; -use FFMpeg\Filters\Audio\SimpleFilter; -use FFMpeg\Filters\Concat\ConcatFilterInterface; -use FFMpeg\Filters\Concat\ConcatFilters; -use FFMpeg\Format\AudioInterface; -use FFMpeg\Format\FormatInterface; -use FFMpeg\Format\VideoInterface; -use Spatie\TemporaryDirectory\TemporaryDirectory; - -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 string $outputPathfile - * @param bool $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 = $this->getTemporaryDirectory()->create(); - $sourcesFile = $fs->path('ffmpeg.concat'); - - // Set the content of this file - $fileStream = @fopen($sourcesFile, 'w'); - - if (false === $fileStream) { - throw new RuntimeException('Cannot open the temporary file.'); - } - - $count_videos = 0; - if (is_array($this->sources) && (count($this->sources) > 0)) { - foreach ($this->sources as $videoPath) { - $line = ''; - - if (0 != $count_videos) { - $line .= "\n"; - } - - $line .= 'file '.addcslashes($videoPath, '\'"\\\0 '); - - fwrite($fileStream, $line); - - ++$count_videos; - } - } else { - throw new InvalidArgumentException('The list of videos is not a valid array.'); - } - fclose($fileStream); - - $commands = [ - '-f', 'concat', '-safe', '0', - '-i', $sourcesFile, - ]; - - // Check if stream copy is activated - if (true === $streamCopy) { - $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, [$outputPathfile]); - - // Execute the command - try { - $this->driver->command($commands); - } catch (ExecutionFailureException $e) { - $this->cleanupTemporaryFile($outputPathfile); - // TODO@v1: paste this line into an `finally` block. - $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 - */ - 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) || (0 == count($this->sources))) { - throw new InvalidArgumentException('The list of videos is not a valid array.'); - } - - // Create the commands variable - $commands = []; - - // Prepare the parameters - $nbSources = 0; - $files = []; - - // 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(['-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')])); - } - if ($format instanceof VideoInterface) { - if (null !== $format->getVideoCodec()) { - $filters->add(new SimpleFilter(['-vcodec', $format->getVideoCodec()])); - } - } - if ($format instanceof AudioInterface) { - if (null !== $format->getAudioCodec()) { - $filters->add(new SimpleFilter(['-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; - } -} diff --git a/src/FFMpeg/Media/Frame.php b/src/FFMpeg/Media/Frame.php deleted file mode 100644 index 2ce2894..0000000 --- a/src/FFMpeg/Media/Frame.php +++ /dev/null @@ -1,137 +0,0 @@ - - * - * 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\Driver\FFMpegDriver; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\FFProbe; -use FFMpeg\Filters\Frame\FrameFilterInterface; -use FFMpeg\Filters\Frame\FrameFilters; - -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 bool $accurate - * @param bool $returnBase64 - * - * @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 = [ - '-y', '-ss', (string) $this->timecode, - '-i', $this->pathfile, - '-vframes', '1', - '-f', $outputFormat, - ]; - } else { - $commands = [ - '-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)); - } - - if (!$returnBase64) { - $commands = array_merge($commands, [$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); - } - } -} diff --git a/src/FFMpeg/Media/Gif.php b/src/FFMpeg/Media/Gif.php deleted file mode 100644 index fbc61cd..0000000 --- a/src/FFMpeg/Media/Gif.php +++ /dev/null @@ -1,137 +0,0 @@ - - * - * 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\Dimension; -use FFMpeg\Coordinate\TimeCode; -use FFMpeg\Driver\FFMpegDriver; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\FFProbe; -use FFMpeg\Filters\Gif\GifFilterInterface; -use FFMpeg\Filters\Gif\GifFilters; - -class Gif extends AbstractMediaType -{ - /** @var TimeCode */ - private $timecode; - /** @var Dimension */ - private $dimension; - /** @var int */ - 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 = [ - '-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, [$pathfile]); - - try { - $this->driver->command($commands); - } catch (ExecutionFailureException $e) { - $this->cleanupTemporaryFile($pathfile); - throw new RuntimeException('Unable to save gif', $e->getCode(), $e); - } - - return $this; - } -} diff --git a/src/FFMpeg/Media/MediaTypeInterface.php b/src/FFMpeg/Media/MediaTypeInterface.php deleted file mode 100644 index 93cd05d..0000000 --- a/src/FFMpeg/Media/MediaTypeInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Media; - -interface MediaTypeInterface -{ - /** - * Returns the available filters. - */ - public function filters(); - - /** - * @return string - */ - public function getPathfile(); -} diff --git a/src/FFMpeg/Media/Spectrum.php b/src/FFMpeg/Media/Spectrum.php deleted file mode 100644 index 488c319..0000000 --- a/src/FFMpeg/Media/Spectrum.php +++ /dev/null @@ -1,601 +0,0 @@ - - * - * 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\Driver\FFMpegDriver; -use FFMpeg\Exception\InvalidArgumentException; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\FFProbe; -use FFMpeg\Filters\Waveform\WaveformFilterInterface; -use FFMpeg\Filters\Waveform\WaveformFilters; - -/** - * Class Spectrum - * Generates an audio spectrum image using FFMPeg's `showspectrumpic` command - * @see https://ffmpeg.org/ffmpeg-filters.html#showspectrumpic - * @author Marcus Bointon - * @package FFMpeg\Media - */ -class Spectrum extends Waveform -{ - const DEFAULT_MODE = 'combined'; - const DEFAULT_COLOR = 'intensity'; - const DEFAULT_SCALE = 'log'; - const DEFAULT_FSCALE = 'lin'; - const DEFAULT_SATURATION = 1.0; - const DEFAULT_WIN_FUNC = 'hann'; - const DEFAULT_ORIENTATION = 'vertical'; - const DEFAULT_GAIN = 1.0; - const DEFAULT_LEGEND = true; - const DEFAULT_ROTATION = 0.0; - const DEFAULT_START = 0; - const DEFAULT_STOP = 0; - - /** - * Whether to generate a `combined` spectrogram for all channels, or a `separate` one for each - * @var string - */ - protected $mode = self::DEFAULT_MODE; - /** - * The color palette to use, see setColor() for options - * @var string - */ - protected $color = self::DEFAULT_COLOR; - /** - * The scale to use for color intensity - * @var string - */ - protected $scale = self::DEFAULT_SCALE; - /** - * The scale to use for the frequency axis - * @var string - */ - protected $fscale = self::DEFAULT_FSCALE; - /** - * A saturation scaling factor, between -10.0 and 10.0. Negative values invert the color palette - * @var float - */ - protected $saturation = self::DEFAULT_SATURATION; - /** - * The windowing function to use when calculating the spectrum. See setWinFunc() for options - * @var string - */ - protected $win_func = self::DEFAULT_WIN_FUNC; - /** - * Frequency axis orientation, `horizontal` or `vertical` - * @var string - */ - protected $orientation = self::DEFAULT_ORIENTATION; - /** - * Gain for calculating color values - * @var float - */ - protected $gain = self::DEFAULT_GAIN; - /** - * Whether to display axes and labels - * @var bool - */ - protected $legend = self::DEFAULT_LEGEND; - /** - * Rotation of colors within the palette, between -1.0 and 1.0 - * @var float - */ - protected $rotation = self::DEFAULT_ROTATION; - /** - * Starting frequency for the spectrum in Hz. Must be positive and not greater than stop frequency - * @var int - */ - protected $start = self::DEFAULT_START; - /** - * Ending frequency for the spectrum in Hz. Must be positive and not less than start frequency - * @var int - */ - protected $stop = self::DEFAULT_STOP; - - /** - * Spectrum constructor. - * - * @param Audio $audio - * @param FFMpegDriver $driver - * @param FFProbe $ffprobe - * @param int $width - * @param int $height - * @param array $colors Note that this is not used, just here for compatibility with the Waveform parent - */ - public function __construct( - Audio $audio, - FFMpegDriver $driver, - FFProbe $ffprobe, - $width, - $height, - $colors = array(self::DEFAULT_COLOR) - ) { - parent::__construct($audio, $driver, $ffprobe, $width, $height); - $this->audio = $audio; - } - - /** - * Set the rendering mode. - * - * @param string $mode `combined` to create a single spectrogram for all channels, or `separate` for each channel separately, all within the same image - * - * @return $this - */ - public function setMode($mode = 'combined') - { - static $modes = array( - 'combined', - 'separate', - ); - if (! in_array($mode, $modes, true)) { - throw new InvalidArgumentException('Unknown mode. Valid values are: ' . implode(', ', $modes)); - } - $this->mode = $mode; - - return $this; - } - - /** - * Get the current rendering mode. - * @return string - */ - public function getMode() - { - return $this->mode; - } - - /** - * Set the color palette to use. - * - * @param string $color One of the available preset palette names: `channel`, `intensity`, `moreland`, `nebulae`, `fire`, `fiery`, `fruit`, `cool`, `magma`, `green`, `viridis`, `plasma`, `cividis`, `terrain`, or `random` to have it pick a random one - * - * @return $this - */ - public function setColor($color = 'intensity') - { - static $modes = array( - 'channel', - 'intensity', - 'moreland', - 'nebulae', - 'fire', - 'fiery', - 'fruit', - 'cool', - 'magma', - 'green', - 'viridis', - 'plasma', - 'cividis', - 'terrain', - ); - if ($color === 'random') { - $this->color = array_rand($modes); - - return $this; - } - if (!in_array($color, $modes, true)) { - throw new InvalidArgumentException('Unknown color mode. Valid values are: ' . implode(', ', $modes)); - } - $this->color = $color; - - return $this; - } - - /** - * Get the current color palette. - * - * @return string - */ - public function getColor() - { - return $this->color; - } - - /** - * Set the scale to use for color intensity - * - * @param string $scale One of `lin`, `sqrt`, `log`, `4thrt`, or `5thrt`. - * - * @return $this - */ - public function setScale($scale = 'log') - { - static $scales = array( - 'lin', - 'sqrt', - 'log', - '4thrt', - '5thrt', - ); - if (! in_array($scale, $scales, true)) { - throw new InvalidArgumentException('Unknown scale. Valid values are: ' . implode(', ', $scales)); - } - $this->scale = $scale; - - return $this; - } - - /** - * Get the current color intensity scale. - * - * @return string - */ - public function getScale() - { - return $this->scale; - } - - /** - * Set the frequency axis scale. - * - * @param string $fscale One of `lin` or `log`. - * - * @return $this - */ - public function setFscale($fscale = 'lin') - { - static $fscales = array( - 'lin', - 'log', - ); - if (! in_array($fscale, $fscales, true)) { - throw new InvalidArgumentException('Unknown fscale. Valid values are: ' . implode(', ', $fscales)); - } - $this->fscale = $fscale; - - return $this; - } - - /** - * Get the current frequency axis scale. - * - * @return string - */ - public function getFscale() - { - return $this->fscale; - } - - /** - * Set the color saturation scaling factor. - * - * @param float $saturation A value between -10.0 and 10.0 to multiply saturation values by. Negative values invert the saturation. - * - * @return $this - */ - public function setSaturation($saturation = 1.0) - { - $saturation = (float)$saturation; - if ($saturation < -10.0 || $saturation > 10.0) { - throw new InvalidArgumentException('Saturation must be between -10.0 and 10.0.'); - } - $this->saturation = $saturation; - - return $this; - } - - /** - * Get the current saturation scaling value. - * - * @return float - */ - public function getSaturation() - { - return $this->saturation; - } - - /** - * Set the windowing function to use when calculating the spectrum. - * - * @param string $win_func One of `rect`, `bartlett`, `hann`, `hanning`, `hamming`, `blackman`, `welch`, `flattop`, `bharris`, `bnuttall`, `bhann`, `sine`, `nuttall`, `lanczos`, `gauss`, `tukey`, `dolph`, `cauchy`, `parzen`, `poisson`, or `bohman`. - * - * @return $this - */ - public function setWinFunc($win_func = 'hann') - { - static $win_funcs = array( - 'rect', - 'bartlett', - 'hann', - 'hanning', - 'hamming', - 'blackman', - 'welch', - 'flattop', - 'bharris', - 'bnuttall', - 'bhann', - 'sine', - 'nuttall', - 'lanczos', - 'gauss', - 'tukey', - 'dolph', - 'cauchy', - 'parzen', - 'poisson', - 'bohman', - ); - if (! in_array($win_func, $win_funcs, true)) { - throw new InvalidArgumentException('Unknown win_func. Valid values are: ' . implode(', ', $win_funcs)); - } - $this->win_func = $win_func; - - return $this; - } - - /** - * Get the current windowing function. - * - * @return string - */ - public function getWinFunc() - { - return $this->win_func; - } - - /** - * Set the orientation of the generated spectrum. - * - * @param string $orientation `vertical` or `horizontal` - * - * @return $this - */ - public function setOrientation($orientation = 'vertical') - { - static $orientations = array( - 'vertical', - 'horizontal', - ); - if (! in_array($orientation, $orientations, true)) { - throw new InvalidArgumentException( - 'Unknown orientation. Valid values are: ' . implode(', ', $orientations) - ); - } - $this->orientation = $orientation; - - return $this; - } - - /** - * Get the current orientation. - * - * @return string - */ - public function getOrientation() - { - return $this->orientation; - } - - /** - * Set the gain used for calculating colour values. - * - * @param float $gain A multiplying factor: Use larger values for files with quieter audio. - * - * @return $this - */ - public function setGain($gain = 1.0) - { - $this->gain = (float)$gain; - - return $this; - } - - /** - * Get the current colour gain factor. - * - * @return float - */ - public function getGain() - { - return $this->gain; - } - - /** - * Turn the graph legends (axes and scales) on and off. - * - * @param bool $legend - * - * @return $this - */ - public function setLegend($legend = true) - { - $this->legend = (bool)$legend; - - return $this; - } - - /** - * Get the current legend state. - * - * @return bool - */ - public function getLegend() - { - return $this->legend; - } - - /** - * Set the color rotation value. This rotates the colour palette, not the resulting image. - * - * @param float $rotation - * - * @return $this - */ - public function setRotation($rotation = 0.0) - { - $rotation = (float)$rotation; - if ($rotation < -1.0 || $rotation > 1.0) { - throw new InvalidArgumentException('Color rotation must be between -1.0 and 1.0.'); - } - $this->rotation = $rotation; - - return $this; - } - - /** - * Get the color palette rotation value. - * - * @return float - */ - public function getRotation() - { - return $this->rotation; - } - - /** - * Set the starting frequency for the spectrum. - * - * @param int $start The starting frequency, in Hz. Must be positive and not greater than the stop frequency. - * - * @return $this - */ - public function setStart($start = 0) - { - $start = (int)abs($start); - if ($start > $this->stop) { - throw new InvalidArgumentException('Start frequency must be lower than stop frequency.'); - } - $this->start = $start; - - return $this; - } - - /** - * Get the current starting frequency. - * - * @return int - */ - public function getStart() - { - return $this->start; - } - - /** - * Set the ending frequency for the spectrum. - * - * @param int $stop The ending frequency, in Hz. Must be positive and not less than the start frequency. - * - * @return $this - */ - public function setStop($stop = 0) - { - $stop = (int)abs($stop); - if ($stop < $this->start) { - throw new InvalidArgumentException('Stop frequency must be higher than start frequency.'); - } - $this->stop = $stop; - - return $this; - } - - /** - * Get the current ending frequency. - * - * @return int - */ - public function getStop() - { - return $this->stop; - } - - /** - * Compile options into a parameter string - * - * @return string - */ - protected function compileOptions() - { - $params = array(); - $params[] = 's=' . $this->width . 'x' . $this->height; - if ($this->mode !== self::DEFAULT_MODE) { - $params[] = 'mode=' . $this->mode; - } - if ($this->color !== self::DEFAULT_COLOR) { - $params[] = 'color=' . $this->color; - } - if ($this->scale !== self::DEFAULT_SCALE) { - $params[] = 'scale=' . $this->scale; - } - if ($this->fscale !== self::DEFAULT_FSCALE) { - $params[] = 'fscale=' . $this->fscale; - } - if ($this->saturation !== self::DEFAULT_SATURATION) { - $params[] = 'saturation=' . $this->saturation; - } - if ($this->win_func !== self::DEFAULT_WIN_FUNC) { - $params[] = 'win_func=' . $this->win_func; - } - if ($this->orientation !== self::DEFAULT_ORIENTATION) { - $params[] = 'orientation=' . $this->orientation; - } - if ($this->gain !== self::DEFAULT_GAIN) { - $params[] = 'gain=' . $this->gain; - } - if ($this->legend !== self::DEFAULT_LEGEND) { - $params[] = 'legend=' . ($this->legend ? '1' : '0'); - } - if ($this->rotation !== self::DEFAULT_ROTATION) { - $params[] = 'rotation=' . $this->rotation; - } - if ($this->start !== self::DEFAULT_START) { - $params[] = 'start=' . $this->start; - } - if ($this->stop !== self::DEFAULT_STOP) { - $params[] = 'stop=' . $this->stop; - } - return implode(':', $params); - } - - /** - * Generates and saves the spectrum in the given filename. - * - * @param string $pathfile - * - * @return Spectrum - * - * @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( - '-y', //Overwrite output files - '-i', //Specify input file - $this->pathfile, - '-filter_complex', //Say we want a complex filter - 'showspectrumpic=' . $this->compileOptions(), //Specify the filter and its params - '-frames:v', //Stop writing output... - 1 // after 1 "frame" - ); - - 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 spectrum', $e->getCode(), $e); - } - - return $this; - } -} diff --git a/src/FFMpeg/Media/Video.php b/src/FFMpeg/Media/Video.php deleted file mode 100644 index 97c2f43..0000000 --- a/src/FFMpeg/Media/Video.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace FFMpeg\Media; - -use FFMpeg\Coordinate\Dimension; -use FFMpeg\Coordinate\TimeCode; - -class Video extends AbstractVideo -{ - /** - * Gets the frame at timecode. - * - * @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 int $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); - } - - /** - * Clips the video at the given time(s). - * - * @param TimeCode $start Start time - * @param TimeCode $duration Duration - * - * @return \FFMpeg\Media\Clip - */ - public function clip(TimeCode $start, TimeCode $duration = null) - { - return new Clip($this, $this->driver, $this->ffprobe, $start, $duration); - } -} diff --git a/src/FFMpeg/Media/Waveform.php b/src/FFMpeg/Media/Waveform.php deleted file mode 100644 index 328c02f..0000000 --- a/src/FFMpeg/Media/Waveform.php +++ /dev/null @@ -1,159 +0,0 @@ - - * - * 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\Driver\FFMpegDriver; -use FFMpeg\Exception\InvalidArgumentException; -use FFMpeg\Exception\RuntimeException; -use FFMpeg\FFProbe; -use FFMpeg\Filters\Waveform\WaveformFilterInterface; -use FFMpeg\Filters\Waveform\WaveformFilters; - -class Waveform extends AbstractMediaType -{ - public const DEFAULT_COLOR = '#000000'; - - /** @var Video */ - protected $audio; - protected $width; - protected $height; - - /** - * @var array - */ - protected $colors; - - public function __construct(Audio $audio, FFMpegDriver $driver, FFProbe $ffprobe, $width, $height, $colors = [self::DEFAULT_COLOR]) - { - parent::__construct($audio->getPathfile(), $driver, $ffprobe); - $this->audio = $audio; - $this->width = $width; - $this->height = $height; - - $this->setColors($colors); - } - - /** - * 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; - } - - /** - * Parameter should be an array containing at least one valid color represented as a HTML color string. For - * example #FFFFFF or #000000. By default the color is set to black. Keep in mind that if you save the waveform - * as jpg file, it will appear completely black and to avoid this you can set the waveform color to white (#FFFFFF). - * Saving waveforms to png is strongly suggested. - */ - public function setColors(array $colors) - { - foreach ($colors as $row => $value) { - if (!preg_match('/^#(?:[0-9a-fA-F]{6})$/', $value)) { - //invalid color - //unset($colors[$row]); - - throw new InvalidArgumentException("The provided color '$value' is invalid"); - } - } - - if (count($colors)) { - $this->colors = $colors; - } - } - - /** - * Returns an array of colors that will be passed to ffmpeg to use for waveform generation. Colors are applied ONLY - * to the waveform. Background cannot be controlled that easily and it is probably easier to save the waveform - * as a transparent png file and then add background of choice. - * - * @return array - */ - public function getColors() - { - return $this->colors; - } - - /** - * Compiles the selected colors into a string, using a pipe separator. - * - * @return string - */ - protected function compileColors() - { - return implode('|', $this->colors); - } - - /** - * 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 = [ - '-y', '-i', $this->pathfile, '-filter_complex', - 'showwavespic=colors='.$this->compileColors().':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, [$pathfile]); - - try { - $this->driver->command($commands); - } catch (ExecutionFailureException $e) { - $this->cleanupTemporaryFile($pathfile); - throw new RuntimeException('Unable to save waveform', $e->getCode(), $e); - } - - return $this; - } -} diff --git a/src/FFMpeg/Media/AdvancedMedia.php b/src/MappableMedia.php similarity index 100% rename from src/FFMpeg/Media/AdvancedMedia.php rename to src/MappableMedia.php