🔥 Delete almost all the code
This commit is contained in:
parent
bda300b69a
commit
001f55939b
118 changed files with 0 additions and 9383 deletions
|
|
@ -1,218 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\BinaryDriver;
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
use Symfony\Component\Process\Process;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenient PHPUnit methods for testing BinaryDriverInterface implementations.
|
|
||||||
*/
|
|
||||||
class BinaryDriverTestCase extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @return ProcessBuilderFactoryInterface
|
|
||||||
*/
|
|
||||||
public function createProcessBuilderFactoryMock()
|
|
||||||
{
|
|
||||||
return $this->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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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());
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\BinaryDriver;
|
|
||||||
|
|
||||||
interface ConfigurationAwareInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the configuration
|
|
||||||
*
|
|
||||||
* @return ConfigurationInterface
|
|
||||||
*/
|
|
||||||
public function getConfiguration();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the configuration
|
|
||||||
*
|
|
||||||
* @param ConfigurationInterface $configuration
|
|
||||||
*/
|
|
||||||
public function setConfiguration(ConfigurationInterface $configuration);
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\BinaryDriver\Exception;
|
|
||||||
|
|
||||||
interface ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\BinaryDriver\Exception;
|
|
||||||
|
|
||||||
class ExecutableNotFoundException extends \RuntimeException implements ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\BinaryDriver\Exception;
|
|
||||||
|
|
||||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\BinaryDriver\Listeners;
|
|
||||||
|
|
||||||
use SplObjectStorage;
|
|
||||||
use Evenement\EventEmitter;
|
|
||||||
|
|
||||||
class Listeners extends EventEmitter
|
|
||||||
{
|
|
||||||
/** @var SplObjectStorage */
|
|
||||||
public $storage;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->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());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Alchemy\BinaryDriver.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace 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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,259 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Coordinate;
|
|
||||||
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
|
|
||||||
// see http://en.wikipedia.org/wiki/List_of_common_resolutions
|
|
||||||
class AspectRatio
|
|
||||||
{
|
|
||||||
// named 4:3 or 1.33:1 Traditional TV
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Coordinate;
|
|
||||||
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Coordinate;
|
|
||||||
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
|
|
||||||
class 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Coordinate;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Coordinate;
|
|
||||||
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
|
|
||||||
class TimeCode
|
|
||||||
{
|
|
||||||
//see http://www.dropframetimecode.org/
|
|
||||||
private $hours;
|
|
||||||
private $minutes;
|
|
||||||
private $seconds;
|
|
||||||
private $frames;
|
|
||||||
|
|
||||||
public function __construct($hours, $minutes, $seconds, $frames)
|
|
||||||
{
|
|
||||||
$this->hours = $hours;
|
|
||||||
$this->minutes = $minutes;
|
|
||||||
$this->seconds = $seconds;
|
|
||||||
$this->frames = $frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return sprintf('%02d:%02d:%02d.%02d', $this->hours, $this->minutes, $this->seconds, $this->frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates timecode from string.
|
|
||||||
*
|
|
||||||
* @param string $timecode
|
|
||||||
*
|
|
||||||
* @return TimeCode
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException In case an invalid timecode is supplied
|
|
||||||
*/
|
|
||||||
public static function fromString($timecode)
|
|
||||||
{
|
|
||||||
$days = 0;
|
|
||||||
|
|
||||||
if (preg_match('/^[0-9]+:[0-9]+:[0-9]+:[0-9]+\.[0-9]+$/', $timecode)) {
|
|
||||||
list($days, $hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d:%d.%d');
|
|
||||||
} elseif (preg_match('/^[0-9]+:[0-9]+:[0-9]+:[0-9]+:[0-9]+$/', $timecode)) {
|
|
||||||
list($days, $hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d:%d:%d');
|
|
||||||
} elseif (preg_match('/^[0-9]+:[0-9]+:[0-9]+\.[0-9]+$/', $timecode)) {
|
|
||||||
list($hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d.%s');
|
|
||||||
} elseif (preg_match('/^[0-9]+:[0-9]+:[0-9]+:[0-9]+$/', $timecode)) {
|
|
||||||
list($hours, $minutes, $seconds, $frames) = sscanf($timecode, '%d:%d:%d:%s');
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException(sprintf('Unable to parse timecode %s', $timecode));
|
|
||||||
}
|
|
||||||
|
|
||||||
$hours += $days * 24;
|
|
||||||
|
|
||||||
return new static($hours, $minutes, $seconds, $frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates timecode from number of seconds.
|
|
||||||
*
|
|
||||||
* @param float $quantity
|
|
||||||
*
|
|
||||||
* @return TimeCode
|
|
||||||
*/
|
|
||||||
public static function fromSeconds($quantity)
|
|
||||||
{
|
|
||||||
$minutes = $hours = $frames = 0;
|
|
||||||
|
|
||||||
$frames = round(100 * ($quantity - floor($quantity)));
|
|
||||||
$seconds = floor($quantity);
|
|
||||||
|
|
||||||
if ($seconds > 59) {
|
|
||||||
$minutes = floor($seconds / 60);
|
|
||||||
$seconds = $seconds % 60;
|
|
||||||
}
|
|
||||||
if ($minutes > 59) {
|
|
||||||
$hours = floor($minutes / 60);
|
|
||||||
$minutes = $minutes % 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new static($hours, $minutes, $seconds, $frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this timecode in seconds.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function toSeconds()
|
|
||||||
{
|
|
||||||
$seconds = 0;
|
|
||||||
|
|
||||||
$seconds += $this->hours * 60 * 60;
|
|
||||||
$seconds += $this->minutes * 60;
|
|
||||||
$seconds += $this->seconds;
|
|
||||||
|
|
||||||
// TODO: Handle frames?
|
|
||||||
|
|
||||||
return (int) $seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function wether `$timecode` is after this one.
|
|
||||||
*
|
|
||||||
* @param TimeCode $timecode The Timecode to compare
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isAfter(TimeCode $timecode)
|
|
||||||
{
|
|
||||||
// convert everything to seconds and compare
|
|
||||||
return $this->toSeconds() > $timecode->toSeconds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Driver;
|
|
||||||
|
|
||||||
use Alchemy\BinaryDriver\AbstractBinary;
|
|
||||||
use Alchemy\BinaryDriver\Configuration;
|
|
||||||
use Alchemy\BinaryDriver\ConfigurationInterface;
|
|
||||||
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException as BinaryDriverExecutableNotFound;
|
|
||||||
use FFMpeg\Exception\ExecutableNotFoundException;
|
|
||||||
use 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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Driver;
|
|
||||||
|
|
||||||
use Alchemy\BinaryDriver\AbstractBinary;
|
|
||||||
use Alchemy\BinaryDriver\Configuration;
|
|
||||||
use Alchemy\BinaryDriver\ConfigurationInterface;
|
|
||||||
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException as BinaryDriverExecutableNotFound;
|
|
||||||
use FFMpeg\Exception\ExecutableNotFoundException;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
class 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Exception;
|
|
||||||
|
|
||||||
interface ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Exception;
|
|
||||||
|
|
||||||
class ExecutableNotFoundException extends RuntimeException
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Exception;
|
|
||||||
|
|
||||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Exception;
|
|
||||||
|
|
||||||
class LogicException extends \LogicException implements ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Exception;
|
|
||||||
|
|
||||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg;
|
|
||||||
|
|
||||||
use Alchemy\BinaryDriver\ConfigurationInterface;
|
|
||||||
use FFMpeg\Driver\FFMpegDriver;
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
use FFMpeg\Exception\RuntimeException;
|
|
||||||
use FFMpeg\Media\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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,287 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg;
|
|
||||||
|
|
||||||
use Alchemy\BinaryDriver\ConfigurationInterface;
|
|
||||||
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
|
|
||||||
use 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe\DataMapping;
|
|
||||||
|
|
||||||
abstract class AbstractData implements \Countable
|
|
||||||
{
|
|
||||||
private $properties;
|
|
||||||
|
|
||||||
public function __construct(array $properties)
|
|
||||||
{
|
|
||||||
$this->properties = $properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if data has property.
|
|
||||||
*
|
|
||||||
* @param string $property
|
|
||||||
*
|
|
||||||
* @return 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe\DataMapping;
|
|
||||||
|
|
||||||
class Format extends AbstractData
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe\DataMapping;
|
|
||||||
|
|
||||||
use FFMpeg\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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe\DataMapping;
|
|
||||||
|
|
||||||
use 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
|
|
||||||
use 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
interface OptionsTesterInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Tells if the given option is supported by ffprobe.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function has($name);
|
|
||||||
}
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
use FFMpeg\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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://ffmpeg.org/ffmpeg-filters.html#anullsrc
|
|
||||||
*/
|
|
||||||
class ANullSrcFilter extends AbstractComplexFilter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $channelLayout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $sampleRate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $nbSamples;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ANullSrcComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @param string|null $channelLayout
|
|
||||||
* @param int|null $sampleRate
|
|
||||||
* @param int|null $nbSamples
|
|
||||||
* @param int $priority
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
$channelLayout = null,
|
|
||||||
$sampleRate = null,
|
|
||||||
$nbSamples = null,
|
|
||||||
$priority = 0
|
|
||||||
) {
|
|
||||||
parent::__construct($priority);
|
|
||||||
$this->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,
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
abstract class AbstractComplexFilter implements ComplexCompatibleFilter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $priority;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AbstractComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @param int $priority
|
|
||||||
*/
|
|
||||||
public function __construct($priority = 0)
|
|
||||||
{
|
|
||||||
$this->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 '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Filters\FilterInterface;
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A filter that can be used inside "-filter_complex" option.
|
|
||||||
*/
|
|
||||||
interface ComplexCompatibleFilter extends FilterInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get name of the filter.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get minimal version of ffmpeg starting with which this filter is supported.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getMinimalFFMpegVersion();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the complex filter to the given media.
|
|
||||||
*
|
|
||||||
* @return string[] an array of arguments
|
|
||||||
*/
|
|
||||||
public function applyComplex(AdvancedMedia $media);
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for the complex compatible filter.
|
|
||||||
*/
|
|
||||||
class ComplexFilterContainer implements ComplexFilterInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $priority;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ComplexCompatibleFilter
|
|
||||||
*/
|
|
||||||
private $baseFilter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $inLabels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $outLabels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @param string $inLabels
|
|
||||||
* @param string $outLabels
|
|
||||||
*/
|
|
||||||
public function __construct($inLabels, ComplexCompatibleFilter $baseFilter, $outLabels)
|
|
||||||
{
|
|
||||||
$this->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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A filter that is completely ready to use inside "-filter_complex" option.
|
|
||||||
*/
|
|
||||||
interface ComplexFilterInterface extends ComplexCompatibleFilter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getInLabels();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getOutLabels();
|
|
||||||
}
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\Dimension;
|
|
||||||
use FFMpeg\Filters\Video\PadFilter;
|
|
||||||
use FFMpeg\Filters\Video\WatermarkFilter;
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
class ComplexFilters
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var AdvancedMedia
|
|
||||||
*/
|
|
||||||
protected $media;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ComplexFilters constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(AdvancedMedia $media)
|
|
||||||
{
|
|
||||||
$this->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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
class CustomComplexFilter extends AbstractComplexFilter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $filter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CustomComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @param string $filter
|
|
||||||
* @param int $priority
|
|
||||||
*/
|
|
||||||
public function __construct($filter, $priority = 0)
|
|
||||||
{
|
|
||||||
parent::__construct($priority);
|
|
||||||
$this->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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://ffmpeg.org/ffmpeg-filters.html#sine
|
|
||||||
*/
|
|
||||||
class SineFilter extends AbstractComplexFilter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $frequency;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $beep_factor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $sample_rate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $samples_per_frame;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SineComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @param string $duration
|
|
||||||
* @param int|null $frequency
|
|
||||||
* @param string|null $beep_factor
|
|
||||||
* @param int|null $sample_rate
|
|
||||||
* @param string|null $samples_per_frame
|
|
||||||
* @param int $priority
|
|
||||||
*/
|
|
||||||
public function __construct($duration, $frequency, $beep_factor, $sample_rate, $samples_per_frame, $priority = 0)
|
|
||||||
{
|
|
||||||
parent::__construct($priority);
|
|
||||||
$this->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,
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,246 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This filter builds various types of computed inputs.
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
class TestSrcFilter extends AbstractComplexFilter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Source returns frames of size 4096x4096 of all rgb colors.
|
|
||||||
*/
|
|
||||||
public const ALLRGB = 'allrgb';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source returns frames of size 4096x4096 of all yuv colors.
|
|
||||||
*/
|
|
||||||
public const ALLYUV = 'allyuv';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source provides an uniformly colored input.
|
|
||||||
*/
|
|
||||||
public const COLOR = 'color';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source provides an identity Hald CLUT.
|
|
||||||
*/
|
|
||||||
public const HALDCLUTSRC = 'haldclutsrc';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source returns unprocessed video frames.
|
|
||||||
* It is mainly useful to be employed in analysis / debugging tools,
|
|
||||||
* or as the source for filters which ignore the input data.
|
|
||||||
*/
|
|
||||||
public const NULLSRC = 'nullsrc';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates a color bars pattern, based on EBU PAL recommendations with 75% color levels.
|
|
||||||
*/
|
|
||||||
public const PAL75BARS = 'pal75bars';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates a color bars pattern, based on EBU PAL recommendations with 100% color levels.
|
|
||||||
*/
|
|
||||||
public const PAL100BARS = 'pal100bars';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates an RGB test pattern useful for detecting RGB vs BGR issues.
|
|
||||||
* You should see a red, green and blue stripe from top to bottom.
|
|
||||||
*/
|
|
||||||
public const RGBTESTSRC = 'rgbtestsrc';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates a color bars pattern, based on the SMPTE Engineering Guideline EG 1-1990.
|
|
||||||
*/
|
|
||||||
public const SMPTEBARS = 'smptebars';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates a color bars pattern, based on the SMPTE RP 219-2002.
|
|
||||||
*/
|
|
||||||
public const SMPTEHDBARS = 'smptehdbars';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates a test video pattern, showing a color pattern, a scrolling gradient and a timestamp.
|
|
||||||
* This is mainly intended for testing purposes.
|
|
||||||
*/
|
|
||||||
public const TESTSRC = 'testsrc';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source is similar to testsrc, but supports more pixel formats instead of just rgb24.
|
|
||||||
* This allows using it as an input for other tests without requiring a format conversion.
|
|
||||||
*/
|
|
||||||
public const TESTSRC2 = 'testsrc2';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source generates an YUV test pattern. You should see a y, cb and cr stripe from top to bottom.
|
|
||||||
*/
|
|
||||||
public const YUVTESTSRC = 'yuvtestsrc';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the level of the Hald CLUT, only available in the haldclutsrc source.
|
|
||||||
* A level of N generates a picture of N*N*N by N*N*N pixels to be used as identity matrix for 3D lookup tables.
|
|
||||||
* Each component is coded on a 1/(N*N) scale.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the color of the source, only available in the color source.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $color;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the size of the sourced video.
|
|
||||||
* This option is not available with the allrgb, allyuv, and haldclutsrc filters.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the frame rate of the sourced video, as the number of frames generated per second.
|
|
||||||
* It has to be a string in the format frame_rate_num/frame_rate_den, an integer number,
|
|
||||||
* a floating point number or a valid video frame rate abbreviation. The default value is "25".
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $rate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the duration of the sourced video.
|
|
||||||
* If not specified, or the expressed duration is negative, the video is supposed to be generated forever.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the sample aspect ratio of the sourced video.
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
private $sar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the alpha (opacity) of the background, only available in the testsrc2 source.
|
|
||||||
* The value must be between 0 (fully transparent) and 255 (fully opaque, the default).
|
|
||||||
*
|
|
||||||
* @var int|null
|
|
||||||
*/
|
|
||||||
private $alpha;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the number of decimals to show in the timestamp, only available in the testsrc source.
|
|
||||||
* The displayed timestamp value will correspond to the original timestamp value multiplied
|
|
||||||
* by the power of 10 of the specified value. Default value is 0.
|
|
||||||
*
|
|
||||||
* @var float|null
|
|
||||||
*/
|
|
||||||
private $decimals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TestSrcComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
* @param int|null $priority
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
$type = self::TESTSRC,
|
|
||||||
$size = '320x240',
|
|
||||||
$duration = null,
|
|
||||||
$sar = null,
|
|
||||||
$rate = null,
|
|
||||||
$level = null,
|
|
||||||
$color = null,
|
|
||||||
$alpha = null,
|
|
||||||
$decimals = null,
|
|
||||||
$priority = 0
|
|
||||||
) {
|
|
||||||
parent::__construct($priority);
|
|
||||||
$this->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,
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\AdvancedMedia;
|
|
||||||
|
|
||||||
use FFMpeg\Media\AdvancedMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* "xstack" filter.
|
|
||||||
* This filter helps you to create a collage from the given videos.
|
|
||||||
* This filter is supported starting from 4.1 ffmpeg version.
|
|
||||||
* (On early versions you can use combinations of hstack and vstack filters).
|
|
||||||
*
|
|
||||||
* @see https://ffmpeg.org/ffmpeg-filters.html#xstack
|
|
||||||
*/
|
|
||||||
class XStackFilter extends AbstractComplexFilter
|
|
||||||
{
|
|
||||||
public const LAYOUT_2X2 = '0_0|0_h0|w0_0|w0_h0';
|
|
||||||
public const LAYOUT_1X4 = '0_0|0_h0|0_h0+h1|0_h0+h1+h2';
|
|
||||||
public const LAYOUT_3X3 = '0_0|0_h0|0_h0+h1|w0_0|w0_h0|w0_h0+h1|w0+w3_0|w0+w3_h0|w0+w3_h0+h1';
|
|
||||||
public const LAYOUT_4X4 = '0_0|0_h0|0_h0+h1|0_h0+h1+h2|w0_0|w0_h0|w0_h0+h1|w0_h0+h1+h2|w0+w4_0|w0+w4_h0|w0+w4_h0+h1|w0+w4_h0+h1+h2|w0+w4+w8_0|w0+w4+w8_h0|w0+w4+w8_h0+h1|w0+w4+w8_h0+h1+h2';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $layout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $inputsCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CustomComplexFilter constructor.
|
|
||||||
*
|
|
||||||
* @param string $layout
|
|
||||||
* @param int $inputsCount
|
|
||||||
* @param int $priority
|
|
||||||
*/
|
|
||||||
public function __construct($layout, $inputsCount, $priority = 0)
|
|
||||||
{
|
|
||||||
parent::__construct($priority);
|
|
||||||
$this->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,
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Audio;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Audio;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Audio;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Audio;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Concat;
|
|
||||||
|
|
||||||
use FFMpeg\Filters\FilterInterface;
|
|
||||||
use FFMpeg\Media\Concat;
|
|
||||||
|
|
||||||
interface ConcatFilterInterface extends FilterInterface
|
|
||||||
{
|
|
||||||
public function apply(Concat $concat);
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Concat;
|
|
||||||
|
|
||||||
use FFMpeg\Media\Concat;
|
|
||||||
|
|
||||||
class ConcatFilters
|
|
||||||
{
|
|
||||||
private $concat;
|
|
||||||
|
|
||||||
public function __construct(Concat $concat)
|
|
||||||
{
|
|
||||||
$this->concat = $concat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters;
|
|
||||||
|
|
||||||
interface FilterInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the priority of the filter.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getPriority();
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Frame;
|
|
||||||
|
|
||||||
use FFMpeg\Filters\FilterInterface;
|
|
||||||
use FFMpeg\Media\Frame;
|
|
||||||
|
|
||||||
interface FrameFilterInterface extends FilterInterface
|
|
||||||
{
|
|
||||||
public function apply(Frame $frame);
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Gif;
|
|
||||||
|
|
||||||
use FFMpeg\Filters\FilterInterface;
|
|
||||||
use FFMpeg\Media\Gif;
|
|
||||||
|
|
||||||
interface GifFilterInterface extends FilterInterface
|
|
||||||
{
|
|
||||||
public function apply(Gif $gif);
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Gif;
|
|
||||||
|
|
||||||
use FFMpeg\Media\Gif;
|
|
||||||
|
|
||||||
class GifFilters
|
|
||||||
{
|
|
||||||
private $gif;
|
|
||||||
|
|
||||||
public function __construct(Gif $gif)
|
|
||||||
{
|
|
||||||
$this->gif = $gif;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\Dimension;
|
|
||||||
use FFMpeg\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(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <romain@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
use FFMpeg\Exception\RuntimeException;
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\Dimension;
|
|
||||||
use FFMpeg\Exception\RuntimeException;
|
|
||||||
use FFMpeg\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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\Dimension;
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
use FFMpeg\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.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\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'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Coordinate\Dimension;
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Video;
|
|
||||||
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
use FFMpeg\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]',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Waveform;
|
|
||||||
|
|
||||||
use FFMpeg\Filters\FilterInterface;
|
|
||||||
use FFMpeg\Media\Waveform;
|
|
||||||
|
|
||||||
interface WaveformFilterInterface extends FilterInterface
|
|
||||||
{
|
|
||||||
public function apply(Waveform $waveform);
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Strime <contact@strime.io>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The AAC audio format.
|
|
||||||
*/
|
|
||||||
class Aac extends DefaultAudio
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->audioCodec = 'libfdk_aac';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getAvailableAudioCodecs()
|
|
||||||
{
|
|
||||||
return ['libfdk_aac'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Audio;
|
|
||||||
|
|
||||||
use Evenement\EventEmitter;
|
|
||||||
use FFMpeg\Exception\InvalidArgumentException;
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Flac audio format.
|
|
||||||
*/
|
|
||||||
class Flac extends DefaultAudio
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->audioCodec = 'flac';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getAvailableAudioCodecs()
|
|
||||||
{
|
|
||||||
return ['flac'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The MP3 audio format.
|
|
||||||
*/
|
|
||||||
class Mp3 extends DefaultAudio
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->audioCodec = 'libmp3lame';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getAvailableAudioCodecs()
|
|
||||||
{
|
|
||||||
return ['libmp3lame'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Audio;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The WAV audio format.
|
|
||||||
*/
|
|
||||||
class Wav extends DefaultAudio
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->audioCodec = 'pcm_s16le';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getAvailableAudioCodecs()
|
|
||||||
{
|
|
||||||
return ['pcm_s16le'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format;
|
|
||||||
|
|
||||||
interface FrameInterface extends FormatInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,260 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\ProgressListener;
|
|
||||||
|
|
||||||
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
|
|
||||||
use Evenement\EventEmitter;
|
|
||||||
use FFMpeg\Exception\RuntimeException;
|
|
||||||
use FFMpeg\FFProbe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Robert Gruendler <r.gruendler@gmail.com>
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\ProgressListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses ffmpeg stderr progress information. An example:.
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* size= 3552kB time=00:03:47.29 bitrate= 128.0kbits/s
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author Robert Gruendler <r.gruendler@gmail.com>
|
|
||||||
*/
|
|
||||||
class AudioProgressListener extends AbstractProgressListener
|
|
||||||
{
|
|
||||||
public function getPattern()
|
|
||||||
{
|
|
||||||
return '/size=(.*?) time=(.*?) /';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\ProgressListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses ffmpeg stderr progress information for video files. An example:.
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* frame= 171 fps=0.0 q=10.0 size= 18kB time=00:00:05.72 bitrate= 26.4kbits/s dup=8 drop=0
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author Robert Gruendler <r.gruendler@gmail.com>
|
|
||||||
*/
|
|
||||||
class VideoProgressListener extends AbstractProgressListener
|
|
||||||
{
|
|
||||||
public function getPattern()
|
|
||||||
{
|
|
||||||
return '/size=(.*?) time=(.*?) /';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <dev.team@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Video;
|
|
||||||
|
|
||||||
use FFMpeg\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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of PHP-FFmpeg.
|
|
||||||
*
|
|
||||||
* (c) Alchemy <info@alchemy.fr>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FFMpeg\Format\Video;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue