parent
					
						
							
								233742db0a
							
						
					
				
			
			
				commit
				
					
						0b871e59e7
					
				
			
		
					 5 changed files with 454 additions and 267 deletions
				
			
		
							
								
								
									
										292
									
								
								src/FFMpeg/Media/AbstractVideo.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								src/FFMpeg/Media/AbstractVideo.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,292 @@
 | 
			
		|||
<?php
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of PHP-FFmpeg.
 | 
			
		||||
 *
 | 
			
		||||
 * (c) Alchemy <info@alchemy.fr>
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view the LICENSE
 | 
			
		||||
 * file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
namespace FFMpeg\Media;
 | 
			
		||||
 | 
			
		||||
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
 | 
			
		||||
use FFMpeg\Filters\Audio\SimpleFilter;
 | 
			
		||||
use FFMpeg\Exception\InvalidArgumentException;
 | 
			
		||||
use FFMpeg\Exception\RuntimeException;
 | 
			
		||||
use FFMpeg\Filters\Video\VideoFilters;
 | 
			
		||||
use FFMpeg\Filters\FilterInterface;
 | 
			
		||||
use FFMpeg\Format\FormatInterface;
 | 
			
		||||
use FFMpeg\Format\ProgressableInterface;
 | 
			
		||||
use FFMpeg\Format\AudioInterface;
 | 
			
		||||
use FFMpeg\Format\VideoInterface;
 | 
			
		||||
use Neutron\TemporaryFilesystem\Manager as FsManager;
 | 
			
		||||
use FFMpeg\Filters\Video\ClipFilter;
 | 
			
		||||
 | 
			
		||||
abstract class AbstractVideo extends Audio
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * FileSystem Manager instance
 | 
			
		||||
     * @var Manager
 | 
			
		||||
     */
 | 
			
		||||
    protected $fs;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * FileSystem Manager ID
 | 
			
		||||
     * @var int
 | 
			
		||||
     */
 | 
			
		||||
    protected $fsId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     * @return VideoFilters
 | 
			
		||||
     */
 | 
			
		||||
    public function filters()
 | 
			
		||||
    {
 | 
			
		||||
        return new VideoFilters($this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     * @return Video
 | 
			
		||||
     */
 | 
			
		||||
    public function addFilter(FilterInterface $filter)
 | 
			
		||||
    {
 | 
			
		||||
        $this->filters->add($filter);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exports the video in the desired format, applies registered filters.
 | 
			
		||||
     *
 | 
			
		||||
     * @param FormatInterface   $format
 | 
			
		||||
     * @param string            $outputPathfile
 | 
			
		||||
     * @return Video
 | 
			
		||||
     * @throws RuntimeException
 | 
			
		||||
     */
 | 
			
		||||
    public function save(FormatInterface $format, $outputPathfile)
 | 
			
		||||
    {
 | 
			
		||||
        $passes = $this->buildCommand($format, $outputPathfile);
 | 
			
		||||
 | 
			
		||||
        $failure = null;
 | 
			
		||||
        $totalPasses = $format->getPasses();
 | 
			
		||||
 | 
			
		||||
        foreach ($passes as $pass => $passCommands) {
 | 
			
		||||
            try {
 | 
			
		||||
                /** add listeners here */
 | 
			
		||||
                $listeners = null;
 | 
			
		||||
 | 
			
		||||
                if ($format instanceof ProgressableInterface) {
 | 
			
		||||
                    $filters = clone $this->filters;
 | 
			
		||||
                    $duration = 0;
 | 
			
		||||
 | 
			
		||||
                    // check the filters of the video, and if the video has the ClipFilter then
 | 
			
		||||
                    // take the new video duration and send to the
 | 
			
		||||
                    // FFMpeg\Format\ProgressListener\AbstractProgressListener class
 | 
			
		||||
                    foreach ($filters as $filter) {
 | 
			
		||||
                        if ($filter instanceof ClipFilter) {
 | 
			
		||||
                            $duration = $filter->getDuration()->toSeconds();
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    $listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses, $duration);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $this->driver->command($passCommands, false, $listeners);
 | 
			
		||||
            } catch (ExecutionFailureException $e) {
 | 
			
		||||
                $failure = $e;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->fs->clean($this->fsId);
 | 
			
		||||
 | 
			
		||||
        if (null !== $failure) {
 | 
			
		||||
            throw new RuntimeException('Encoding failed', $failure->getCode(), $failure);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * NOTE: This method is different to the Audio's one, because Video is using passes.
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     */
 | 
			
		||||
    public function getFinalCommand(FormatInterface $format, $outputPathfile)
 | 
			
		||||
    {
 | 
			
		||||
        $finalCommands = array();
 | 
			
		||||
 | 
			
		||||
        foreach ($this->buildCommand($format, $outputPathfile) as $pass => $passCommands) {
 | 
			
		||||
            $finalCommands[] = implode(' ', $passCommands);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->fs->clean($this->fsId);
 | 
			
		||||
 | 
			
		||||
        return $finalCommands;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * **NOTE:** This creates passes instead of a single command!
 | 
			
		||||
     *
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     * @return string[][]
 | 
			
		||||
     */
 | 
			
		||||
    protected function buildCommand(FormatInterface $format, $outputPathfile)
 | 
			
		||||
    {
 | 
			
		||||
        $commands = $this->basePartOfCommand();
 | 
			
		||||
 | 
			
		||||
        $filters = clone $this->filters;
 | 
			
		||||
        $filters->add(new SimpleFilter($format->getExtraParams(), 10));
 | 
			
		||||
 | 
			
		||||
        if ($this->driver->getConfiguration()->has('ffmpeg.threads')) {
 | 
			
		||||
            $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads'))));
 | 
			
		||||
        }
 | 
			
		||||
        if ($format instanceof VideoInterface) {
 | 
			
		||||
            if (null !== $format->getVideoCodec()) {
 | 
			
		||||
                $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec())));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ($format instanceof AudioInterface) {
 | 
			
		||||
            if (null !== $format->getAudioCodec()) {
 | 
			
		||||
                $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec())));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($filters as $filter) {
 | 
			
		||||
            $commands = array_merge($commands, $filter->apply($this, $format));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($format instanceof VideoInterface) {
 | 
			
		||||
            $commands[] = '-b:v';
 | 
			
		||||
            $commands[] = $format->getKiloBitrate() . 'k';
 | 
			
		||||
            $commands[] = '-refs';
 | 
			
		||||
            $commands[] = '6';
 | 
			
		||||
            $commands[] = '-coder';
 | 
			
		||||
            $commands[] = '1';
 | 
			
		||||
            $commands[] = '-sc_threshold';
 | 
			
		||||
            $commands[] = '40';
 | 
			
		||||
            $commands[] = '-flags';
 | 
			
		||||
            $commands[] = '+loop';
 | 
			
		||||
            $commands[] = '-me_range';
 | 
			
		||||
            $commands[] = '16';
 | 
			
		||||
            $commands[] = '-subq';
 | 
			
		||||
            $commands[] = '7';
 | 
			
		||||
            $commands[] = '-i_qfactor';
 | 
			
		||||
            $commands[] = '0.71';
 | 
			
		||||
            $commands[] = '-qcomp';
 | 
			
		||||
            $commands[] = '0.6';
 | 
			
		||||
            $commands[] = '-qdiff';
 | 
			
		||||
            $commands[] = '4';
 | 
			
		||||
            $commands[] = '-trellis';
 | 
			
		||||
            $commands[] = '1';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($format instanceof AudioInterface) {
 | 
			
		||||
            if (null !== $format->getAudioKiloBitrate()) {
 | 
			
		||||
                $commands[] = '-b:a';
 | 
			
		||||
                $commands[] = $format->getAudioKiloBitrate() . 'k';
 | 
			
		||||
            }
 | 
			
		||||
            if (null !== $format->getAudioChannels()) {
 | 
			
		||||
                $commands[] = '-ac';
 | 
			
		||||
                $commands[] = $format->getAudioChannels();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the user passed some additional parameters
 | 
			
		||||
        if ($format instanceof VideoInterface) {
 | 
			
		||||
            if (null !== $format->getAdditionalParameters()) {
 | 
			
		||||
                foreach ($format->getAdditionalParameters() as $additionalParameter) {
 | 
			
		||||
                    $commands[] = $additionalParameter;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Merge Filters into one command
 | 
			
		||||
        $videoFilterVars = $videoFilterProcesses = array();
 | 
			
		||||
        for ($i = 0; $i < count($commands); $i++) {
 | 
			
		||||
            $command = $commands[$i];
 | 
			
		||||
            if ($command == '-vf') {
 | 
			
		||||
                $commandSplits = explode(";", $commands[$i + 1]);
 | 
			
		||||
                if (count($commandSplits) == 1) {
 | 
			
		||||
                    $commandSplit = $commandSplits[0];
 | 
			
		||||
                    $command = trim($commandSplit);
 | 
			
		||||
                    if (preg_match("/^\[in\](.*?)\[out\]$/is", $command, $match)) {
 | 
			
		||||
                        $videoFilterProcesses[] = $match[1];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $videoFilterProcesses[] = $command;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    foreach ($commandSplits as $commandSplit) {
 | 
			
		||||
                        $command = trim($commandSplit);
 | 
			
		||||
                        if (preg_match("/^\[[^\]]+\](.*?)\[[^\]]+\]$/is", $command, $match)) {
 | 
			
		||||
                            $videoFilterProcesses[] = $match[1];
 | 
			
		||||
                        } else {
 | 
			
		||||
                            $videoFilterVars[] = $command;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                unset($commands[$i]);
 | 
			
		||||
                unset($commands[$i + 1]);
 | 
			
		||||
                $i++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $videoFilterCommands = $videoFilterVars;
 | 
			
		||||
        $lastInput = 'in';
 | 
			
		||||
        foreach ($videoFilterProcesses as $i => $process) {
 | 
			
		||||
            $command = '[' . $lastInput . ']';
 | 
			
		||||
            $command .= $process;
 | 
			
		||||
            $lastInput = 'p' . $i;
 | 
			
		||||
            if ($i === (count($videoFilterProcesses) - 1)) {
 | 
			
		||||
                $command .= '[out]';
 | 
			
		||||
            } else {
 | 
			
		||||
                $command .= '[' . $lastInput . ']';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $videoFilterCommands[] = $command;
 | 
			
		||||
        }
 | 
			
		||||
        $videoFilterCommand = implode(';', $videoFilterCommands);
 | 
			
		||||
 | 
			
		||||
        if ($videoFilterCommand) {
 | 
			
		||||
            $commands[] = '-vf';
 | 
			
		||||
            $commands[] = $videoFilterCommand;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->fs = FsManager::create();
 | 
			
		||||
        $this->fsId = uniqid('ffmpeg-passes');
 | 
			
		||||
        $passPrefix = $this->fs->createTemporaryDirectory(0777, 50, $this->fsId) . '/' . uniqid('pass-');
 | 
			
		||||
        $passes = array();
 | 
			
		||||
        $totalPasses = $format->getPasses();
 | 
			
		||||
 | 
			
		||||
        if (!$totalPasses) {
 | 
			
		||||
            throw new InvalidArgumentException('Pass number should be a positive value.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for ($i = 1; $i <= $totalPasses; $i++) {
 | 
			
		||||
            $pass = $commands;
 | 
			
		||||
 | 
			
		||||
            if ($totalPasses > 1) {
 | 
			
		||||
                $pass[] = '-pass';
 | 
			
		||||
                $pass[] = $i;
 | 
			
		||||
                $pass[] = '-passlogfile';
 | 
			
		||||
                $pass[] = $passPrefix;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $pass[] = $outputPathfile;
 | 
			
		||||
 | 
			
		||||
            $passes[] = $pass;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $passes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return base part of command.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    protected function basePartOfCommand()
 | 
			
		||||
    {
 | 
			
		||||
        return array('-y', '-i', $this->pathfile);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								src/FFMpeg/Media/Clip.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/FFMpeg/Media/Clip.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
<?php
 | 
			
		||||
namespace FFMpeg\Media;
 | 
			
		||||
 | 
			
		||||
use FFMpeg\Driver\FFMpegDriver;
 | 
			
		||||
use FFMpeg\FFProbe;
 | 
			
		||||
use FFMpeg\Coordinate\TimeCode;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Video clip.
 | 
			
		||||
 *
 | 
			
		||||
 * Use input seeking, see http://trac.ffmpeg.org/wiki/Seeking
 | 
			
		||||
 */
 | 
			
		||||
class Clip extends Video
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /** @var TimeCode Start time */
 | 
			
		||||
    private $start;
 | 
			
		||||
 | 
			
		||||
    /** @var TimeCode Duration */
 | 
			
		||||
    private $duration;
 | 
			
		||||
 | 
			
		||||
    /** @var Video Parrent video */
 | 
			
		||||
    private $video;
 | 
			
		||||
 | 
			
		||||
    public function __construct(Video $video, FFMpegDriver $driver, FFProbe $ffprobe, TimeCode $start, TimeCode $duration = null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->start = $start;
 | 
			
		||||
        $this->duration = $duration;
 | 
			
		||||
        $this->video = $video;
 | 
			
		||||
 | 
			
		||||
        parent::__construct($video->getPathfile(), $driver, $ffprobe);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the video related to the frame.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Video
 | 
			
		||||
     */
 | 
			
		||||
    public function getVideo()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->video;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return base part of command.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    protected function basePartOfCommand()
 | 
			
		||||
    {
 | 
			
		||||
        $arr = array('-y', '-ss', (string) $this->start, '-i', $this->pathfile);
 | 
			
		||||
 | 
			
		||||
        if (is_null($this->duration) === false) {
 | 
			
		||||
            $arr[] = '-t';
 | 
			
		||||
            $arr[] = (string) $this->duration;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $arr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This file is part of PHP-FFmpeg.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -8,278 +7,13 @@
 | 
			
		|||
 * For the full copyright and license information, please view the LICENSE
 | 
			
		||||
 * file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace FFMpeg\Media;
 | 
			
		||||
 | 
			
		||||
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
 | 
			
		||||
use FFMpeg\Coordinate\TimeCode;
 | 
			
		||||
use FFMpeg\Coordinate\Dimension;
 | 
			
		||||
use FFMpeg\Filters\Audio\SimpleFilter;
 | 
			
		||||
use FFMpeg\Exception\InvalidArgumentException;
 | 
			
		||||
use FFMpeg\Exception\RuntimeException;
 | 
			
		||||
use FFMpeg\Filters\Video\VideoFilters;
 | 
			
		||||
use FFMpeg\Filters\FilterInterface;
 | 
			
		||||
use FFMpeg\Format\FormatInterface;
 | 
			
		||||
use FFMpeg\Format\ProgressableInterface;
 | 
			
		||||
use FFMpeg\Format\AudioInterface;
 | 
			
		||||
use FFMpeg\Format\VideoInterface;
 | 
			
		||||
use Neutron\TemporaryFilesystem\Manager as FsManager;
 | 
			
		||||
use FFMpeg\Filters\Video\ClipFilter;
 | 
			
		||||
 | 
			
		||||
class Video extends Audio
 | 
			
		||||
class Video extends AbstractVideo
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * FileSystem Manager instance
 | 
			
		||||
     * @var Manager
 | 
			
		||||
     */
 | 
			
		||||
    protected $fs;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * FileSystem Manager ID
 | 
			
		||||
     * @var int
 | 
			
		||||
     */
 | 
			
		||||
    protected $fsId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     * @return VideoFilters
 | 
			
		||||
     */
 | 
			
		||||
    public function filters()
 | 
			
		||||
    {
 | 
			
		||||
        return new VideoFilters($this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     * @return Video
 | 
			
		||||
     */
 | 
			
		||||
    public function addFilter(FilterInterface $filter)
 | 
			
		||||
    {
 | 
			
		||||
        $this->filters->add($filter);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exports the video in the desired format, applies registered filters.
 | 
			
		||||
     *
 | 
			
		||||
     * @param FormatInterface   $format
 | 
			
		||||
     * @param string            $outputPathfile
 | 
			
		||||
     * @return Video
 | 
			
		||||
     * @throws RuntimeException
 | 
			
		||||
     */
 | 
			
		||||
    public function save(FormatInterface $format, $outputPathfile)
 | 
			
		||||
    {
 | 
			
		||||
        $passes = $this->buildCommand($format, $outputPathfile);
 | 
			
		||||
 | 
			
		||||
        $failure = null;
 | 
			
		||||
        $totalPasses = $format->getPasses();
 | 
			
		||||
 | 
			
		||||
        foreach ($passes as $pass => $passCommands) {
 | 
			
		||||
            try {
 | 
			
		||||
                /** add listeners here */
 | 
			
		||||
                $listeners = null;
 | 
			
		||||
        
 | 
			
		||||
                if ($format instanceof ProgressableInterface) {
 | 
			
		||||
                    $filters = clone $this->filters;
 | 
			
		||||
                    $duration = 0;
 | 
			
		||||
                    
 | 
			
		||||
                    // check the filters of the video, and if the video has the ClipFilter then
 | 
			
		||||
                    // take the new video duration and send to the
 | 
			
		||||
                    // FFMpeg\Format\ProgressListener\AbstractProgressListener class
 | 
			
		||||
                    foreach ($filters as $filter) {
 | 
			
		||||
                        if($filter instanceof ClipFilter){
 | 
			
		||||
                            $duration = $filter->getDuration()->toSeconds();
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    $listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses, $duration);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $this->driver->command($passCommands, false, $listeners);
 | 
			
		||||
            } catch (ExecutionFailureException $e) {
 | 
			
		||||
                $failure = $e;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->fs->clean($this->fsId);
 | 
			
		||||
 | 
			
		||||
        if (null !== $failure) {
 | 
			
		||||
            throw new RuntimeException('Encoding failed', $failure->getCode(), $failure);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * NOTE: This method is different to the Audio's one, because Video is using passes.
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     */
 | 
			
		||||
    public function getFinalCommand(FormatInterface $format, $outputPathfile) {
 | 
			
		||||
        $finalCommands = array();
 | 
			
		||||
 | 
			
		||||
        foreach($this->buildCommand($format, $outputPathfile) as $pass => $passCommands) {
 | 
			
		||||
            $finalCommands[] = implode(' ', $passCommands);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->fs->clean($this->fsId);
 | 
			
		||||
 | 
			
		||||
        return $finalCommands;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * **NOTE:** This creates passes instead of a single command!
 | 
			
		||||
     *
 | 
			
		||||
     * @inheritDoc
 | 
			
		||||
     * @return string[][]
 | 
			
		||||
     */
 | 
			
		||||
    protected function buildCommand(FormatInterface $format, $outputPathfile) {
 | 
			
		||||
        $commands = array('-y', '-i', $this->pathfile);
 | 
			
		||||
 | 
			
		||||
        $filters = clone $this->filters;
 | 
			
		||||
        $filters->add(new SimpleFilter($format->getExtraParams(), 10));
 | 
			
		||||
 | 
			
		||||
        if ($this->driver->getConfiguration()->has('ffmpeg.threads')) {
 | 
			
		||||
            $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads'))));
 | 
			
		||||
        }
 | 
			
		||||
        if ($format instanceof VideoInterface) {
 | 
			
		||||
            if (null !== $format->getVideoCodec()) {
 | 
			
		||||
                $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec())));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ($format instanceof AudioInterface) {
 | 
			
		||||
            if (null !== $format->getAudioCodec()) {
 | 
			
		||||
                $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec())));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($filters as $filter) {
 | 
			
		||||
            $commands = array_merge($commands, $filter->apply($this, $format));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($format instanceof VideoInterface) {
 | 
			
		||||
            $commands[] = '-b:v';
 | 
			
		||||
            $commands[] = $format->getKiloBitrate() . 'k';
 | 
			
		||||
            $commands[] = '-refs';
 | 
			
		||||
            $commands[] = '6';
 | 
			
		||||
            $commands[] = '-coder';
 | 
			
		||||
            $commands[] = '1';
 | 
			
		||||
            $commands[] = '-sc_threshold';
 | 
			
		||||
            $commands[] = '40';
 | 
			
		||||
            $commands[] = '-flags';
 | 
			
		||||
            $commands[] = '+loop';
 | 
			
		||||
            $commands[] = '-me_range';
 | 
			
		||||
            $commands[] = '16';
 | 
			
		||||
            $commands[] = '-subq';
 | 
			
		||||
            $commands[] = '7';
 | 
			
		||||
            $commands[] = '-i_qfactor';
 | 
			
		||||
            $commands[] = '0.71';
 | 
			
		||||
            $commands[] = '-qcomp';
 | 
			
		||||
            $commands[] = '0.6';
 | 
			
		||||
            $commands[] = '-qdiff';
 | 
			
		||||
            $commands[] = '4';
 | 
			
		||||
            $commands[] = '-trellis';
 | 
			
		||||
            $commands[] = '1';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($format instanceof AudioInterface) {
 | 
			
		||||
            if (null !== $format->getAudioKiloBitrate()) {
 | 
			
		||||
                $commands[] = '-b:a';
 | 
			
		||||
                $commands[] = $format->getAudioKiloBitrate() . 'k';
 | 
			
		||||
            }
 | 
			
		||||
            if (null !== $format->getAudioChannels()) {
 | 
			
		||||
                $commands[] = '-ac';
 | 
			
		||||
                $commands[] = $format->getAudioChannels();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the user passed some additional parameters
 | 
			
		||||
        if ($format instanceof VideoInterface) {
 | 
			
		||||
            if (null !== $format->getAdditionalParameters()) {
 | 
			
		||||
                foreach ($format->getAdditionalParameters() as $additionalParameter) {
 | 
			
		||||
                    $commands[] = $additionalParameter;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Merge Filters into one command
 | 
			
		||||
        $videoFilterVars = $videoFilterProcesses = array();
 | 
			
		||||
        for($i=0;$i<count($commands);$i++) {
 | 
			
		||||
            $command = $commands[$i];
 | 
			
		||||
            if ( $command == '-vf' ) {
 | 
			
		||||
                $commandSplits = explode(";", $commands[$i + 1]);
 | 
			
		||||
                if ( count($commandSplits) == 1 ) {
 | 
			
		||||
                    $commandSplit = $commandSplits[0];
 | 
			
		||||
                    $command = trim($commandSplit);
 | 
			
		||||
                    if ( preg_match("/^\[in\](.*?)\[out\]$/is", $command, $match) ) {
 | 
			
		||||
                        $videoFilterProcesses[] = $match[1];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $videoFilterProcesses[] = $command;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    foreach($commandSplits as $commandSplit) {
 | 
			
		||||
                        $command = trim($commandSplit);
 | 
			
		||||
                        if ( preg_match("/^\[[^\]]+\](.*?)\[[^\]]+\]$/is", $command, $match) ) {
 | 
			
		||||
                            $videoFilterProcesses[] = $match[1];
 | 
			
		||||
                        } else {
 | 
			
		||||
                            $videoFilterVars[] = $command;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                unset($commands[$i]);
 | 
			
		||||
                unset($commands[$i + 1]);
 | 
			
		||||
                $i++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $videoFilterCommands = $videoFilterVars;
 | 
			
		||||
        $lastInput = 'in';
 | 
			
		||||
        foreach($videoFilterProcesses as $i => $process) {
 | 
			
		||||
            $command = '[' . $lastInput .']';
 | 
			
		||||
            $command .= $process;
 | 
			
		||||
            $lastInput = 'p' . $i;
 | 
			
		||||
            if($i === (count($videoFilterProcesses) - 1)) {
 | 
			
		||||
                $command .= '[out]';
 | 
			
		||||
            } else {
 | 
			
		||||
                $command .= '[' . $lastInput . ']';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $videoFilterCommands[] = $command;
 | 
			
		||||
        }
 | 
			
		||||
        $videoFilterCommand = implode(';', $videoFilterCommands);
 | 
			
		||||
 | 
			
		||||
        if($videoFilterCommand) {
 | 
			
		||||
            $commands[] = '-vf';
 | 
			
		||||
            $commands[] = $videoFilterCommand;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->fs = FsManager::create();
 | 
			
		||||
        $this->fsId = uniqid('ffmpeg-passes');
 | 
			
		||||
        $passPrefix = $this->fs->createTemporaryDirectory(0777, 50, $this->fsId) . '/' . uniqid('pass-');
 | 
			
		||||
        $passes = array();
 | 
			
		||||
        $totalPasses = $format->getPasses();
 | 
			
		||||
 | 
			
		||||
        if(!$totalPasses) {
 | 
			
		||||
            throw new InvalidArgumentException('Pass number should be a positive value.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for($i = 1; $i <= $totalPasses; $i++) {
 | 
			
		||||
            $pass = $commands;
 | 
			
		||||
 | 
			
		||||
            if ($totalPasses > 1) {
 | 
			
		||||
                $pass[] = '-pass';
 | 
			
		||||
                $pass[] = $i;
 | 
			
		||||
                $pass[] = '-passlogfile';
 | 
			
		||||
                $pass[] = $passPrefix;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $pass[] = $outputPathfile;
 | 
			
		||||
 | 
			
		||||
            $passes[] = $pass;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $passes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the frame at timecode.
 | 
			
		||||
| 
						 | 
				
			
			@ -315,4 +49,16 @@ class Video extends Audio
 | 
			
		|||
    {
 | 
			
		||||
        return new Concat($sources, $this->driver, $this->ffprobe);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clips the video at the given time(s).
 | 
			
		||||
     *
 | 
			
		||||
     * @param TimeCode $start Start time
 | 
			
		||||
     * @param TimeCode $duration Duration
 | 
			
		||||
     * @return \FFMpeg\Media\Clip
 | 
			
		||||
     */
 | 
			
		||||
    public function clip(TimeCode $start, TimeCode $duration = null)
 | 
			
		||||
    {
 | 
			
		||||
        return new Clip($this, $this->driver, $this->ffprobe, $start, $duration);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue