| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 14:34:53 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | namespace FFMpeg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  | use FFMpeg\Exception\InvalidArgumentException; | 
					
						
							|  |  |  | use FFMpeg\Exception\LogicException; | 
					
						
							|  |  |  | use FFMpeg\Exception\RuntimeException; | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  | use FFMpeg\Format\AudioInterface; | 
					
						
							|  |  |  | use FFMpeg\Format\VideoInterface; | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  | use FFMpeg\Format\Video\Resamplable as VideoResamplable; | 
					
						
							|  |  |  | use FFMpeg\Format\Video\Resizable as VideoResizable; | 
					
						
							|  |  |  | use FFMpeg\Format\Video\Transcodable as VideoTranscodable; | 
					
						
							|  |  |  | use FFMpeg\Format\Audio\Resamplable as AudioResamplable; | 
					
						
							|  |  |  | use FFMpeg\Format\Audio\Transcodable as AudioTranscodable; | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  | use FFMpeg\Helper\HelperInterface; | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  | use Symfony\Component\Process\Process; | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  | use Symfony\Component\Process\ProcessBuilder; | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * FFMpeg driver | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @author Romain Neutron imprec@gmail.com | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | class FFMpeg extends Binary | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     protected $pathfile; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |      * @var FFProbe | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected $prober; | 
					
						
							| 
									
										
										
										
											2012-06-14 20:11:17 +02:00
										 |  |  |     protected $threads = 1; | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @var HelperInterface[] | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected $helpers = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-30 18:44:09 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Destructor | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |     public function __destruct() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->prober = null; | 
					
						
							|  |  |  |         parent::__destruct(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @param HelperInterface $helper | 
					
						
							|  |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function attachHelper(HelperInterface $helper) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->helpers[] = $helper; | 
					
						
							|  |  |  |         $helper->setProber($this->prober); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // ensure the helpers have the path to the file in case
 | 
					
						
							|  |  |  |         // they need to probe for format information
 | 
					
						
							|  |  |  |         if ($this->pathfile !== null) { | 
					
						
							|  |  |  |             $helper->open($this->pathfile); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-14 20:11:17 +02:00
										 |  |  |     public function setThreads($threads) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($threads > 64 || $threads < 1) { | 
					
						
							|  |  |  |             throw new InvalidArgumentException('Invalid `threads` value ; threads must fit in range 1 - 64'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->threads = (int) $threads; | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-14 20:11:17 +02:00
										 |  |  |         return $this; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-14 20:11:17 +02:00
										 |  |  |     public function getThreads() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->threads; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Opens a file in order to be processed | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-05-25 16:22:16 +02:00
										 |  |  |      * @param  string                   $pathfile A pathfile | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |      * @throws InvalidArgumentException | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     public function open($pathfile) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         if (!file_exists($pathfile)) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             $this->logger->addError(sprintf('FFmpeg failed to open %s', $pathfile)); | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             throw new InvalidArgumentException(sprintf('File %s does not exists', $pathfile)); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->logger->addInfo(sprintf('FFmpeg opens %s', $pathfile)); | 
					
						
							|  |  |  |         $this->pathfile = $pathfile; | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |         foreach ($this->helpers as $helper) { | 
					
						
							|  |  |  |             $helper->open($pathfile); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |         return $this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Set a prober | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function setProber(FFProbe $prober) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->prober = $prober; | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |         return $this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Close a file | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function close() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |         $this->logger->addInfo(sprintf('FFmpeg closes %s', $this->pathfile)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |         $this->pathfile = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this; | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2012-05-30 18:44:09 +02:00
										 |  |  |      * Extract an image from a media file | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |      * @param  integer|string   $time     The time where to take the snapshot, time could either be in second or in hh:mm:ss[.xxx] form. | 
					
						
							|  |  |  |      * @param  string           $output   The pathfile where to write | 
					
						
							|  |  |  |      * @param  Boolean          $accurate Whether to decode the whole video until position or seek and extract. See -ss option in FFMpeg manual (http://ffmpeg.org/ffmpeg.html#Main-options)
 | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |      * @throws RuntimeException | 
					
						
							|  |  |  |      * @throws LogicException | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |     public function extractImage($time, $output, $accurate = false) | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         if (!$this->pathfile) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             throw new LogicException('No file open'); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |         /** | 
					
						
							|  |  |  |          * @see http://ffmpeg.org/ffmpeg.html#Main-options
 | 
					
						
							|  |  |  |          */ | 
					
						
							| 
									
										
										
										
											2012-12-13 18:26:20 +01:00
										 |  |  |         if (!$accurate) { | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |             $options = array( | 
					
						
							|  |  |  |                 $this->binary, '-ss', $time, | 
					
						
							|  |  |  |                 '-i', $this->pathfile, | 
					
						
							|  |  |  |                 '-vframes', '1', | 
					
						
							|  |  |  |                 '-f', 'image2', $output | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $options = array( | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |                 $this->binary, | 
					
						
							|  |  |  |                 '-i', $this->pathfile, | 
					
						
							|  |  |  |                 '-vframes', '1', '-ss', $time, | 
					
						
							|  |  |  |                 '-f', 'image2', $output | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |             ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-13 13:03:55 +01:00
										 |  |  |         $builder = ProcessBuilder::create($options); | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $process = $builder->getProcess(); | 
					
						
							| 
									
										
										
										
											2013-02-03 20:05:25 +01:00
										 |  |  |         $process->setTimeout($this->timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $this->logger->addInfo(sprintf('FFmpeg executes command %s', $process->getCommandline())); | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |             $process->run(array($this, 'transcodeCallback')); | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         } catch (\RuntimeException $e) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         if (!$process->isSuccessful()) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             $this->logger->addError(sprintf('FFmpeg command failed : %s', $process->getErrorOutput())); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |             $this->cleanupTemporaryFile($output); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             throw new RuntimeException('Failed to extract image'); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |         $this->logger->addInfo(sprintf('FFmpeg command successful')); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |         return $this; | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Encode the file to the specified format | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |      * @param  AudioInterface   $format         The output format | 
					
						
							| 
									
										
										
										
											2012-05-25 16:22:16 +02:00
										 |  |  |      * @param  string           $outputPathfile The pathfile where to write | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |      * @throws RuntimeException | 
					
						
							|  |  |  |      * @throws LogicException | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |     public function encode(AudioInterface $format, $outputPathfile) | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         if (!$this->pathfile) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             throw new LogicException('No file open'); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         switch (true) { | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |             case $format instanceof VideoInterface: | 
					
						
							| 
									
										
										
										
											2012-06-14 20:11:17 +02:00
										 |  |  |                 $this->encodeVideo($format, $outputPathfile); | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |             case $format instanceof AudioInterface: | 
					
						
							| 
									
										
										
										
											2012-06-14 20:11:17 +02:00
										 |  |  |                 $this->encodeAudio($format, $outputPathfile); | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |                 break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |         return $this; | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Encode to audio | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-05-25 20:54:54 +02:00
										 |  |  |      * @param  Audio            $format         The output format | 
					
						
							| 
									
										
										
										
											2012-05-25 16:22:16 +02:00
										 |  |  |      * @param  string           $outputPathfile The pathfile where to write | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |      * @throws RuntimeException | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |     protected function encodeAudio(AudioInterface $format, $outputPathfile) | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $builder = ProcessBuilder::create(array( | 
					
						
							|  |  |  |                 $this->binary, | 
					
						
							|  |  |  |                 '-y', '-i', | 
					
						
							|  |  |  |                 $this->pathfile, | 
					
						
							|  |  |  |                 '-threads', $this->threads, | 
					
						
							|  |  |  |                 '-ab', $format->getKiloBitrate() . 'k ', | 
					
						
							|  |  |  |             )); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         foreach ($format->getExtraParams() as $parameter) { | 
					
						
							|  |  |  |             $builder->add($parameter); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  |         if ($format instanceof AudioTranscodable) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             $builder->add('-acodec')->add($format->getAudioCodec()); | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-05-31 15:54:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  |         if ($format instanceof AudioResamplable) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             $builder->add('-ac')->add(2)->add('-ar')->add($format->getAudioSampleRate()); | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $builder->add($outputPathfile); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $process = $builder->getProcess(); | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $this->logger->addInfo(sprintf('FFmpeg executes command %s', $process->getCommandLine())); | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |             $process->run(array($this, 'transcodeCallback')); | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         } catch (\RuntimeException $e) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         if (!$process->isSuccessful()) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             $this->logger->addInfo(sprintf('FFmpeg command failed')); | 
					
						
							|  |  |  |             throw new RuntimeException(sprintf('Encoding failed : %s', $process->getErrorOutput())); | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |         $this->logger->addInfo(sprintf('FFmpeg command successful')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |         return $this; | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Encode to video | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |      * @param  VideoInterface   $format         The output format | 
					
						
							| 
									
										
										
										
											2012-05-25 16:22:16 +02:00
										 |  |  |      * @param  string           $outputPathfile The pathfile where to write | 
					
						
							| 
									
										
										
										
											2012-04-18 10:36:29 +02:00
										 |  |  |      * @return \FFMpeg\FFMpeg | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |      * @throws RuntimeException | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-10-31 00:53:26 +01:00
										 |  |  |     protected function encodeVideo(VideoInterface $format, $outputPathfile) | 
					
						
							| 
									
										
										
										
											2012-04-13 14:15:56 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $builder = ProcessBuilder::create(array( | 
					
						
							|  |  |  |                 $this->binary, '-y', '-i', | 
					
						
							|  |  |  |                 $this->pathfile | 
					
						
							|  |  |  |             )); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         foreach ($format->getExtraParams() as $parameter) { | 
					
						
							|  |  |  |             $builder->add($parameter); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-05-25 18:24:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  |         if ($format instanceof VideoResizable) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             if (!$this->prober) { | 
					
						
							| 
									
										
										
										
											2012-06-06 11:12:40 +02:00
										 |  |  |                 throw new LogicException('You must set a valid prober if you use a resizable format'); | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-31 15:54:28 +02:00
										 |  |  |             $result = json_decode($this->prober->probeStreams($this->pathfile), true); | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |             $originalWidth = $originalHeight = null; | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |             foreach ($result as $stream) { | 
					
						
							| 
									
										
										
										
											2012-05-31 15:54:28 +02:00
										 |  |  |                 foreach ($stream as $name => $value) { | 
					
						
							|  |  |  |                     if ($name == 'width') { | 
					
						
							| 
									
										
										
										
											2012-05-31 16:12:51 +02:00
										 |  |  |                         $originalWidth = $value; | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |                         continue; | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2012-06-01 19:03:00 +02:00
										 |  |  |                     if ($name == 'height') { | 
					
						
							| 
									
										
										
										
											2012-05-31 16:12:51 +02:00
										 |  |  |                         $originalHeight = $value; | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |                         continue; | 
					
						
							| 
									
										
										
										
											2012-05-29 10:32:37 +02:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-01 19:03:00 +02:00
										 |  |  |             if ($originalHeight !== null && $originalWidth !== null) { | 
					
						
							|  |  |  |                 $this->logger->addInfo(sprintf('Read dimension for resizin succesful : %s x %s', $originalWidth, $originalHeight)); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 $this->logger->addInfo(sprintf('Read dimension for resizin failed !')); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2012-06-06 11:12:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |             if ($originalHeight !== null && $originalWidth !== null) { | 
					
						
							| 
									
										
										
										
											2012-05-30 16:53:36 +02:00
										 |  |  |                 $dimensions = $format->getComputedDimensions($originalWidth, $originalHeight); | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-30 16:53:36 +02:00
										 |  |  |                 $width = $this->getMultiple($dimensions->getWidth(), 16); | 
					
						
							|  |  |  |                 $height = $this->getMultiple($dimensions->getHeight(), 16); | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |                 $builder->add('-s')->add($width . 'x' . $height); | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if ($format instanceof VideoResamplable) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             $builder->add('-r')->add($format->getFrameRate()); | 
					
						
							| 
									
										
										
										
											2012-06-06 11:12:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             /** | 
					
						
							|  |  |  |              * @see http://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping | 
					
						
							|  |  |  |              */ | 
					
						
							|  |  |  |             if ($format->supportBFrames()) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |                 $builder->add('-b_strategy') | 
					
						
							|  |  |  |                     ->add('1') | 
					
						
							|  |  |  |                     ->add('-bf') | 
					
						
							|  |  |  |                     ->add('3') | 
					
						
							|  |  |  |                     ->add('-g') | 
					
						
							|  |  |  |                     ->add($format->getGOPSize()); | 
					
						
							| 
									
										
										
										
											2012-06-06 11:12:40 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-05-30 12:23:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  |         if ($format instanceof VideoTranscodable) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             $builder->add('-vcodec')->add($format->getVideoCodec()); | 
					
						
							| 
									
										
										
										
											2012-05-25 18:24:37 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $builder->add('-b')->add($format->getKiloBitrate() . 'k') | 
					
						
							|  |  |  |             ->add('-threads')->add($this->threads) | 
					
						
							|  |  |  |             ->add('-refs')->add('6')->add('-coder')->add('1')->add('-qmin') | 
					
						
							|  |  |  |             ->add('10')->add('-qmax')->add('51') | 
					
						
							|  |  |  |             ->add('-sc_threshold')->add('40')->add('-flags')->add('+loop') | 
					
						
							|  |  |  |             ->add('-me_range')->add('16')->add('-subq')->add('7') | 
					
						
							|  |  |  |             ->add('-i_qfactor')->add('0.71')->add('-qcomp')->add('0.6') | 
					
						
							|  |  |  |             ->add('-qdiff')->add('4') | 
					
						
							|  |  |  |             ->add('-trellis')->add('1')->add('-qscale')->add('1') | 
					
						
							|  |  |  |             ->add('-ab')->add('92k'); | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-12 15:23:54 +01:00
										 |  |  |         if ($format instanceof AudioTranscodable) { | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             $builder->add('-acodec')->add($format->getAudioCodec()); | 
					
						
							| 
									
										
										
										
											2012-05-30 15:06:53 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $tmpFile = new \SplFileInfo(tempnam(sys_get_temp_dir(), 'temp') . '.' . pathinfo($outputPathfile, PATHINFO_EXTENSION)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $pass1 = $builder; | 
					
						
							|  |  |  |         $pass2 = clone $builder; | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         $passes[] = $pass1 | 
					
						
							|  |  |  |             ->add('-pass')->add('1')->add('-an')->add($tmpFile->getPathname()) | 
					
						
							|  |  |  |             ->getProcess(); | 
					
						
							|  |  |  |         $passes[] = $pass2 | 
					
						
							|  |  |  |             ->add('-pass')->add('2')->add('-ac')->add('2') | 
					
						
							|  |  |  |             ->add('-ar')->add('44100')->add($outputPathfile) | 
					
						
							|  |  |  |             ->getProcess(); | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         foreach ($passes as $process) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |             $this->logger->addInfo(sprintf('FFmpeg executes command %s', $process->getCommandline())); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |             try { | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |                 $process->run(array($this, 'transcodeCallback')); | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |             } catch (\RuntimeException $e) { | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->cleanupTemporaryFile($tmpFile->getPathname()); | 
					
						
							|  |  |  |         $this->cleanupTemporaryFile(getcwd() . '/ffmpeg2pass-0.log'); | 
					
						
							| 
									
										
										
										
											2012-05-30 18:46:11 +02:00
										 |  |  |         $this->cleanupTemporaryFile(getcwd() . '/av2pass-0.log'); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |         $this->cleanupTemporaryFile(getcwd() . '/ffmpeg2pass-0.log.mbtree'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-08 14:20:59 +02:00
										 |  |  |         if (!$process->isSuccessful()) { | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |             $this->logger->addInfo(sprintf('FFmpeg command failed')); | 
					
						
							|  |  |  |             throw new RuntimeException(sprintf('Encoding failed : %s', $process->getErrorOutput())); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |         $this->logger->addInfo(sprintf('FFmpeg command successful')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-17 16:33:36 +02:00
										 |  |  |         return $this; | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-25 15:40:20 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * The main transcoding callback, delegates the content to the helpers. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $channel (stdio|stderr) | 
					
						
							|  |  |  |      * @param string $content the current line of the ffmpeg output | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function transcodeCallback($channel, $content) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         foreach ($this->helpers as $helper) { | 
					
						
							|  |  |  |             $helper->transcodeCallback($channel, $content); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Removes unnecessary file | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $pathfile | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     protected function cleanupTemporaryFile($pathfile) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         if (file_exists($pathfile) && is_writable($pathfile)) { | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |             unlink($pathfile); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-28 19:46:49 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Returns the nearest multiple for a value | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param  integer $value | 
					
						
							|  |  |  |      * @param  integer $multiple | 
					
						
							|  |  |  |      * @return integer | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function getMultiple($value, $multiple) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $modulo = $value % $multiple; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $ret = (int) $multiple; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $halfDistance = $multiple / 2; | 
					
						
							|  |  |  |         if ($modulo <= $halfDistance) | 
					
						
							|  |  |  |             $bound = 'bottom'; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             $bound = 'top'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch ($bound) { | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |             case 'top': | 
					
						
							|  |  |  |                 $ret = $value + $multiple - $modulo; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case 'bottom': | 
					
						
							|  |  |  |                 $ret = $value - $modulo; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($ret < $multiple) { | 
					
						
							|  |  |  |             $ret = (int) $multiple; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return (int) $ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2012-05-25 16:21:16 +02:00
										 |  |  |      * {@inheritdoc} | 
					
						
							| 
									
										
										
										
											2012-04-13 15:12:43 +02:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return string | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     protected static function getBinaryName() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2012-05-11 00:30:02 +02:00
										 |  |  |         return array('avconv', 'ffmpeg'); | 
					
						
							| 
									
										
										
										
											2012-04-13 10:20:54 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } |