├── .gitignore ├── .editorconfig ├── src ├── SubtitleInterface.php ├── Exception │ ├── InvalidFilterException.php │ ├── TranscoderException.php │ ├── ExecutableNotFoundException.php │ └── ExecutionFailureException.php ├── Filter │ ├── AudioFilterInterface.php │ ├── FrameFilterInterface.php │ ├── FilterInterface.php │ ├── FilterChainInterface.php │ ├── Overlay.php │ ├── Graph.php │ ├── Cut.php │ ├── AudioDelay.php │ ├── Resize.php │ ├── Crop.php │ ├── SimpleFilter.php │ ├── FilterChain.php │ └── Text.php ├── Format │ ├── SubtitleFormatInterface.php │ ├── Srt.php │ ├── Png.php │ ├── Bmp.php │ ├── Ppm.php │ ├── Flac.php │ ├── Oga.php │ ├── Aac.php │ ├── Jpeg.php │ ├── Mp3.php │ ├── SubtitleFormat.php │ ├── FrameFormatInterface.php │ ├── VideoFormatInterface.php │ ├── Flv.php │ ├── Mkv.php │ ├── FormatInterface.php │ ├── Gif.php │ ├── AudioFormatInterface.php │ ├── FormatTrait.php │ ├── FrameFormat.php │ ├── AudioFormat.php │ └── VideoFormat.php ├── Stream │ ├── SubtitleStreamInterface.php │ ├── VideoStreamInterface.php │ ├── SubtitleStream.php │ ├── FrameStreamInterface.php │ ├── AudioStreamInterface.php │ ├── EnumerationInterface.php │ ├── VideoStream.php │ ├── FrameStream.php │ ├── AudioStream.php │ ├── StreamInterface.php │ └── Collection.php ├── Event │ ├── EmitterTrait.php │ ├── EmitterInterface.php │ └── EventProgress.php ├── Preset │ ├── PresetInterface.php │ ├── FilePreset.php │ └── Preset.php ├── Traits │ ├── ConvertEncodingTrait.php │ ├── OptionsAwareTrait.php │ ├── FilePathAwareTrait.php │ └── MetadataTrait.php ├── FrameInterface.php ├── AudioInterface.php ├── Service │ ├── DecoderInterface.php │ ├── EncoderInterface.php │ ├── ServiceFactory.php │ ├── ServiceFactoryInterface.php │ ├── EncoderQueue.php │ └── Heap.php ├── Point.php ├── Codec.php ├── TranscodeInterface.php ├── Dimension.php ├── Subtitle.php ├── TimeInterval.php ├── Frame.php ├── Audio.php ├── VideoInterface.php └── Video.php ├── phpunit.xml ├── tests ├── TimeIntervalTest.php ├── Format │ └── AudioFormatTest.php ├── Traits │ ├── OptionsAwareTraitTest.php │ ├── MetadataTraitTest.php │ └── FilePathAwareTraitTest.php ├── Stream │ └── StreamTraitTest.php ├── PointTest.php ├── DimensionTest.php └── CodecTest.php ├── LICENSE ├── composer.json └── bin └── ffmpeg_fmt.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /vendor/ 3 | nbproject 4 | out 5 | gen 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 4 6 | indent_style = tab 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /src/SubtitleInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Interface SubtitleInterface. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | interface SubtitleInterface extends TranscodeInterface 21 | { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Exception/InvalidFilterException.php: -------------------------------------------------------------------------------- 1 | 10 | * 11 | * @license http://opensource.org/licenses/MIT MIT 12 | * @copyright Copyright (c) 2017 Dmitry Arhitector 13 | */ 14 | namespace Arhitector\Transcoder\Exception; 15 | 16 | /** 17 | * Invalid filter. 18 | * 19 | * @package Arhitector\Transcoder\Exception 20 | */ 21 | class InvalidFilterException extends TranscoderException 22 | { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/Filter/AudioFilterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | /** 16 | * Interface AudioFilterInterface. 17 | * 18 | * @package Arhitector\Transcoder\Filter 19 | */ 20 | interface AudioFilterInterface extends FilterInterface 21 | { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Filter/FrameFilterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | /** 16 | * Interface FrameFilterInterface. 17 | * 18 | * @package Arhitector\Transcoder\Filter 19 | */ 20 | interface FrameFilterInterface extends FilterInterface 21 | { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Exception/TranscoderException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Exception; 14 | 15 | /** 16 | * An exception during transcoding. 17 | * 18 | * @package Arhitector\Transcoder\Exception 19 | */ 20 | class TranscoderException extends \RuntimeException 21 | { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Format/SubtitleFormatInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | /** 16 | * Interface SubtitleFormatInterface. 17 | * 18 | * @package Arhitector\Transcoder\Format 19 | */ 20 | interface SubtitleFormatInterface extends FormatInterface 21 | { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Stream/SubtitleStreamInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Interface SubtitleStreamInterface. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | interface SubtitleStreamInterface extends StreamInterface 21 | { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Event/EmitterTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Event; 14 | 15 | use League\Event\EmitterTrait as EventEmitterTrait; 16 | 17 | /** 18 | * Class EmitterTrait. 19 | * 20 | * @package Arhitector\Transcoder\Event 21 | */ 22 | trait EmitterTrait 23 | { 24 | use EventEmitterTrait; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/Preset/PresetInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Preset; 14 | 15 | /** 16 | * Interface PresetInterface. 17 | * 18 | * @package Arhitector\Transcoder\Preset 19 | */ 20 | interface PresetInterface 21 | { 22 | 23 | /** 24 | * Get the options. 25 | * 26 | * @return array 27 | */ 28 | public function toArray(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Stream/VideoStreamInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Interface VideoStreamInterface. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | interface VideoStreamInterface extends FrameStreamInterface 21 | { 22 | 23 | /** 24 | * Get frame rate value. 25 | * 26 | * @return float 27 | */ 28 | public function getFrameRate(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | tests/ 17 | 18 | 19 | 20 | 21 | src/ 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Stream/SubtitleStream.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Class SubtitleStream. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | class SubtitleStream implements SubtitleStreamInterface 21 | { 22 | use StreamTrait; 23 | 24 | /** 25 | * Returns a bit mask of type. 26 | * 27 | * @return int 28 | */ 29 | public function getType() 30 | { 31 | return self::STREAM_SUBTITLE; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Stream/FrameStreamInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Interface FrameStreamInterface. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | interface FrameStreamInterface extends StreamInterface 21 | { 22 | 23 | /** 24 | * Get width value. 25 | * 26 | * @return int 27 | */ 28 | public function getWidth(); 29 | 30 | /** 31 | * Get height value. 32 | * 33 | * @return int 34 | */ 35 | public function getHeight(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/Format/Srt.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class Srt. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Srt extends SubtitleFormat 23 | { 24 | /** 25 | * SubtitleFormat constructor. 26 | * 27 | * @param Codec|string $codec 28 | */ 29 | public function __construct($codec = null) 30 | { 31 | parent::__construct($codec); 32 | 33 | $this->setExtensions(['srt']); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/Stream/AudioStreamInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Interface AudioStreamInterface. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | interface AudioStreamInterface extends StreamInterface 21 | { 22 | 23 | /** 24 | * Get channels value. 25 | * 26 | * @return int 27 | */ 28 | public function getChannels(); 29 | 30 | /** 31 | * Get sample rate value. 32 | * 33 | * @return string 34 | */ 35 | public function getFrequency(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /tests/TimeIntervalTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests; 14 | 15 | use Arhitector\Transcoder\TimeInterval; 16 | 17 | /** 18 | * Class TimeIntervalTest 19 | * 20 | * @package Arhitector\Transcoder\Tests 21 | */ 22 | class TimeIntervalTest extends \PHPUnit_Framework_TestCase 23 | { 24 | 25 | public function testGettersSuccessful() 26 | { 27 | $timeInterval = new TimeInterval(120); 28 | 29 | $this->assertEquals(0, $timeInterval->getSeconds()); 30 | $this->assertEquals(120, $timeInterval->toSeconds()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Traits/ConvertEncodingTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Traits; 14 | 15 | /** 16 | * Class ConvertEncodingTrait. 17 | * 18 | * @package Arhitector\Transcoder\Traits 19 | */ 20 | trait ConvertEncodingTrait 21 | { 22 | 23 | /** 24 | * Convert character encoding. 25 | * 26 | * @param string $text 27 | * 28 | * @return string 29 | */ 30 | protected function convertEncoding($text) 31 | { 32 | // UTF 8 for Linux and OSX 33 | return mb_convert_encoding($text, stripos(PHP_OS, 'WIN') === false ? 'UTF-8' : 'WINDOWS-1251'); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/Event/EmitterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Event; 14 | 15 | use League\Event\EventInterface; 16 | use League\Event\ListenerAcceptorInterface; 17 | use League\Event\ListenerInterface; 18 | 19 | /** 20 | * Interface EmitterInterface. 21 | * 22 | * @package Arhitector\Transcoder\Event 23 | */ 24 | interface EmitterInterface extends ListenerAcceptorInterface 25 | { 26 | 27 | /** 28 | * Emit an event. 29 | * 30 | * @param string|EventInterface $event 31 | * 32 | * @return EventInterface 33 | */ 34 | public function emit($event); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/Filter/FilterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\TranscodeInterface; 17 | 18 | /** 19 | * Interface FilterInterface. 20 | * 21 | * @package Arhitector\Transcoder\Filter 22 | */ 23 | interface FilterInterface 24 | { 25 | 26 | /** 27 | * Apply filter. 28 | * 29 | * @param TranscodeInterface $media 30 | * @param FormatInterface $format 31 | * 32 | * @return array 33 | */ 34 | public function apply(TranscodeInterface $media, FormatInterface $format); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/Format/Png.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class Png. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Png extends FrameFormat 23 | { 24 | 25 | /** 26 | * Png constructor. 27 | * 28 | * @param Codec|string $codec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($codec = 'png') 33 | { 34 | parent::__construct($codec); 35 | 36 | $this->setExtensions(['png']); 37 | $this->setAvailableVideoCodecs(['apng', 'png']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/FrameInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Interface FrameInterface. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | interface FrameInterface extends TranscodeInterface 21 | { 22 | 23 | /** 24 | * Returns the video codec. 25 | * 26 | * @return Codec|null 27 | */ 28 | public function getVideoCodec(); 29 | 30 | /** 31 | * Get width value. 32 | * 33 | * @return int 34 | */ 35 | public function getWidth(); 36 | 37 | /** 38 | * Get height value. 39 | * 40 | * @return int 41 | */ 42 | public function getHeight(); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/Format/Bmp.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Bmp picture format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Bmp extends FrameFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $codec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($codec = 'bmp') 33 | { 34 | parent::__construct($codec); 35 | 36 | $this->setExtensions(['bmp']); 37 | $this->setAvailableVideoCodecs(['bmp']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Format/Ppm.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Ppm picture format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Ppm extends FrameFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $codec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($codec = 'ppm') 33 | { 34 | parent::__construct($codec); 35 | 36 | $this->setExtensions(['ppm']); 37 | $this->setAvailableVideoCodecs(['ppm']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Format/Flac.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Flac audio format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Flac extends AudioFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $codec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($codec = 'flac') 33 | { 34 | parent::__construct($codec); 35 | 36 | $this->setExtensions(['flac']); 37 | $this->setAvailableAudioCodecs(['flac', 'flaccl']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Stream/EnumerationInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Interface EnumerationInterface. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | interface EnumerationInterface 21 | { 22 | 23 | /** 24 | * @var int Type of audio stream. 25 | */ 26 | const STREAM_AUDIO = 1; 27 | 28 | /** 29 | * @var int Type of frame stream. 30 | */ 31 | const STREAM_FRAME = 2; 32 | 33 | /** 34 | * @var int Type of video stream. 35 | */ 36 | const STREAM_VIDEO = 4; 37 | 38 | /** 39 | * @var int Type of subtitle stream. 40 | */ 41 | const STREAM_SUBTITLE = 8; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/Format/Oga.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Oga audio format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Oga extends AudioFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $audioCodec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($audioCodec = 'vorbis') 33 | { 34 | parent::__construct($audioCodec); 35 | 36 | $this->setExtensions(['oga']); 37 | $this->setAvailableAudioCodecs(['vorbis', 'libvorbis']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Format/Aac.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Aac audio format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Aac extends AudioFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $audioCodec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($audioCodec = 'aac') 33 | { 34 | parent::__construct($audioCodec); 35 | 36 | $this->setExtensions(['aac']); 37 | $this->setAvailableAudioCodecs(['libfdk_aac', 'libfaac', 'aac', 'libvo_aacenc', 'faac']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Format/Jpeg.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Jpeg picture format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Jpeg extends FrameFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $codec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($codec = 'mjpeg') 33 | { 34 | parent::__construct($codec); 35 | 36 | $this->setExtensions(['jpg', 'jpeg']); 37 | $this->setAvailableVideoCodecs(['jpeg2000', 'libopenjpeg', 'jpegls', 'ljpeg', 'mjpeg']); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/Format/Mp3.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class Mp3. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Mp3 extends AudioFormat 23 | { 24 | 25 | /** 26 | * AudioFormat constructor. 27 | * 28 | * @param Codec|string $audioCodec 29 | * 30 | * @throws \InvalidArgumentException 31 | */ 32 | public function __construct($audioCodec = 'libmp3lame') 33 | { 34 | $this->setExtensions(['mp2', 'mp3', 'm2a']); 35 | $this->setAvailableAudioCodecs(['libmp3lame', 'libshine', 'mp3', 'mp3pro', 'lame']); 36 | 37 | parent::__construct($audioCodec); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Dmitry Arhitector 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arhitector/transcoder", 3 | "description": "Tools to transcoding/encoding audio or video, inspect and convert media formats", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": [ 7 | "ffmpeg", 8 | "video", 9 | "transcoder", 10 | "audio", 11 | "converting", 12 | "transcoding" 13 | ], 14 | "homepage": "https://github.com/jack-theripper/transcoder", 15 | "authors": [ 16 | { 17 | "name": "Dmitry Arhitector", 18 | "email": "dmitry.arhitector@yandex.ru" 19 | } 20 | ], 21 | "autoload": { 22 | "psr-4": { 23 | "Arhitector\\Transcoder\\": "src" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "Arhitector\\Transcoder\\Tests\\": "tests" 28 | }, 29 | "require": { 30 | "php": ">=5.6", 31 | "ralouphie/mimey": "^1.0", 32 | "league/event": "^2.1", 33 | "symfony/process": "^3.2" 34 | }, 35 | "require-dev": { 36 | "phpunit/phpunit": "5.*" 37 | }, 38 | "config": { 39 | "bin-dir": "vendor/bin", 40 | "sort-packages": true 41 | }, 42 | "extra": { 43 | "branch-alias": { 44 | "dev-master": "1.0-dev" 45 | } 46 | }, 47 | "scripts": { 48 | "test": "phpunit --colors=auto" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Traits/OptionsAwareTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Traits; 14 | 15 | /** 16 | * Class OptionsAwareTrait. 17 | * 18 | * @package Arhitector\Transcoder\Traits 19 | */ 20 | trait OptionsAwareTrait 21 | { 22 | 23 | /** 24 | * @var array The options. 25 | */ 26 | protected $options = []; 27 | 28 | /** 29 | * Gets the options. 30 | * 31 | * @return array 32 | */ 33 | public function getOptions() 34 | { 35 | return $this->options; 36 | } 37 | 38 | /** 39 | * Sets the options value. 40 | * 41 | * @param array $options 42 | * 43 | * @return $this 44 | */ 45 | public function setOptions(array $options) 46 | { 47 | $this->options = $options; 48 | 49 | return $this; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/AudioInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Interface AudioInterface. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | interface AudioInterface extends TranscodeInterface 21 | { 22 | 23 | /** 24 | * Gets the audio channels value. 25 | * 26 | * @return int 27 | */ 28 | public function getAudioChannels(); 29 | 30 | /** 31 | * Gets the audio kilo bitrate value. 32 | * 33 | * @return int 34 | */ 35 | public function getAudioKiloBitrate(); 36 | 37 | /** 38 | * Returns the audio codec. 39 | * 40 | * @return Codec|null 41 | */ 42 | public function getAudioCodec(); 43 | 44 | /** 45 | * Get sample frequency value. 46 | * 47 | * @return int 48 | */ 49 | public function getFrequency(); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Service/DecoderInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Service; 14 | 15 | use Arhitector\Transcoder\TranscodeInterface; 16 | 17 | /** 18 | * Interface DecoderInterface. 19 | * 20 | * @package Arhitector\Transcoder\Service 21 | */ 22 | interface DecoderInterface 23 | { 24 | 25 | /** 26 | * Demultiplexing. 27 | * 28 | * @param TranscodeInterface $media 29 | * 30 | * @return \stdClass 31 | */ 32 | public function demuxing(TranscodeInterface $media); 33 | 34 | /** 35 | * Gets the options. 36 | * 37 | * @return array 38 | */ 39 | public function getOptions(); 40 | 41 | /** 42 | * Sets the options value. 43 | * 44 | * @param array $options 45 | * 46 | * @return $this 47 | */ 48 | public function setOptions(array $options); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/Format/SubtitleFormat.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class SubtitleFormat. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class SubtitleFormat implements SubtitleFormatInterface 23 | { 24 | use FormatTrait; 25 | 26 | /** 27 | * @var Codec Subtitle codec value. 28 | */ 29 | protected $codec; 30 | 31 | /** 32 | * SubtitleFormat constructor. 33 | * 34 | * @param Codec|string $codec 35 | */ 36 | public function __construct($codec = null) 37 | { 38 | if ($codec !== null) 39 | { 40 | if ( ! $codec instanceof Codec) 41 | { 42 | $codec = new Codec($codec, ''); 43 | } 44 | 45 | $this->codec = $codec; 46 | } 47 | } 48 | 49 | /** 50 | * Returns the number of passes. 51 | * 52 | * @return int 53 | */ 54 | public function getPasses() 55 | { 56 | return 1; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /tests/Format/AudioFormatTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | use Arhitector\Transcoder\Format\AudioFormat; 17 | 18 | /** 19 | * Class AudioFormatTest. 20 | * 21 | * @package Arhitector\Transcoder\Tests\Format 22 | */ 23 | class AudioFormatTest extends \PHPUnit_Framework_TestCase 24 | { 25 | 26 | public function testConstructor() 27 | { 28 | new AudioFormat(); 29 | new AudioFormat('codec string'); 30 | new AudioFormat(new Codec('codec string')); 31 | } 32 | 33 | /** 34 | * @expectedException \InvalidArgumentException 35 | */ 36 | public function testFailureConstructor() 37 | { 38 | new AudioFormat([]); 39 | new AudioFormat(new \stdClass); 40 | } 41 | 42 | public function testGetters() 43 | { 44 | $format = new AudioFormat('codec string'); 45 | 46 | $this->assertInstanceOf(Codec::class, $format->getAudioCodec()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Filter/FilterChainInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | 17 | /** 18 | * Interface FilterChainInterface. 19 | * 20 | * @package Arhitector\Transcoder\Filter 21 | */ 22 | interface FilterChainInterface extends AudioFilterInterface, FrameFilterInterface 23 | { 24 | 25 | /** 26 | * Add a new filter. 27 | * 28 | * @param FilterInterface $filter 29 | * @param int $priority range 0-99. 30 | * 31 | * @return FilterChainInterface 32 | * @throws InvalidFilterException 33 | */ 34 | public function addFilter(FilterInterface $filter, $priority = 0); 35 | 36 | /** 37 | * Attach other chains as input. 38 | * 39 | * @param string $label 40 | * 41 | * @return FilterChainInterface 42 | */ 43 | public function addInputLabel($label); 44 | 45 | /** 46 | * Attach other chains as output. 47 | * 48 | * @param string $label 49 | * 50 | * @return FilterChainInterface 51 | */ 52 | public function addOutputLabel($label); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Service/EncoderInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Service; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\TranscodeInterface; 17 | 18 | /** 19 | * Interface EncoderInterface. 20 | * 21 | * @package Arhitector\Transcoder\Service 22 | */ 23 | interface EncoderInterface 24 | { 25 | 26 | /** 27 | * Constructs and returns the iterator with instances of 'Process'. 28 | * 29 | * @param TranscodeInterface $media it may be a stream or media wrapper. 30 | * @param FormatInterface $format new format. 31 | * @param array $options 32 | * 33 | * @return \Iterator|\Symfony\Component\Process\Process[] 34 | */ 35 | public function transcoding(TranscodeInterface $media, FormatInterface $format, array $options = []); 36 | 37 | /** 38 | * Gets the options. 39 | * 40 | * @return array 41 | */ 42 | public function getOptions(); 43 | 44 | /** 45 | * Sets the options value. 46 | * 47 | * @param array $options 48 | * 49 | * @return $this 50 | */ 51 | public function setOptions(array $options); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/Format/FrameFormatInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Interface FrameFormatInterface. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | interface FrameFormatInterface extends FormatInterface 23 | { 24 | 25 | /** 26 | * Get width value. 27 | * 28 | * @return int 29 | */ 30 | public function getWidth(); 31 | 32 | /** 33 | * Get height value. 34 | * 35 | * @return int 36 | */ 37 | public function getHeight(); 38 | 39 | /** 40 | * Get the video/frame codec. 41 | * 42 | * @return Codec 43 | */ 44 | public function getVideoCodec(); 45 | 46 | /** 47 | * Sets the video/frame codec, should be in the available ones, otherwise an exception is thrown. 48 | * 49 | * @param Codec $codec 50 | * 51 | * @return FrameFormatInterface 52 | * @throws \InvalidArgumentException 53 | */ 54 | public function setVideoCodec(Codec $codec); 55 | 56 | /** 57 | * Get available codecs. 58 | * 59 | * @return string[] 60 | */ 61 | public function getAvailableVideoCodecs(); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/Stream/VideoStream.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Class VideoStream. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | class VideoStream extends FrameStream implements VideoStreamInterface 21 | { 22 | 23 | /** 24 | * @var float Frame rate. 25 | */ 26 | protected $frameRate; 27 | 28 | /** 29 | * Get frame rate value. 30 | * 31 | * @return float 32 | */ 33 | public function getFrameRate() 34 | { 35 | return $this->frameRate; 36 | } 37 | 38 | /** 39 | * Returns a bit mask of type. 40 | * 41 | * @return int 42 | */ 43 | public function getType() 44 | { 45 | return self::STREAM_VIDEO; 46 | } 47 | 48 | /** 49 | * Set frame rate value. 50 | * 51 | * @param float $frameRate 52 | * 53 | * @return VideoStreamInterface 54 | * @throws \InvalidArgumentException 55 | */ 56 | protected function setFrameRate($frameRate) 57 | { 58 | if ( ! is_float($frameRate) || $frameRate < 0) 59 | { 60 | throw new \InvalidArgumentException('Wrong frame rate value.'); 61 | } 62 | 63 | $this->frameRate = $frameRate; 64 | 65 | return $this; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/Format/VideoFormatInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | /** 16 | * Interface VideoFormatInterface. 17 | * 18 | * @package Arhitector\Transcoder\Format 19 | */ 20 | interface VideoFormatInterface extends FrameFormatInterface, AudioFormatInterface 21 | { 22 | 23 | /** 24 | * Get the video bitrate value. 25 | * 26 | * @return int 27 | */ 28 | public function getVideoBitrate(); 29 | 30 | /** 31 | * Set the bitrate value. 32 | * 33 | * @param int $bitrate 34 | * 35 | * @return VideoFormatInterface 36 | * @throws \InvalidArgumentException 37 | */ 38 | public function setVideoBitrate($bitrate); 39 | 40 | /** 41 | * Sets the number of passes. 42 | * 43 | * @param int $passes 44 | * 45 | * @return VideoFormatInterface 46 | */ 47 | public function setPasses($passes); 48 | 49 | /** 50 | * Get the frame rate value. 51 | * 52 | * @return float 53 | */ 54 | public function getFrameRate(); 55 | 56 | /** 57 | * Set the frame rate value. 58 | * 59 | * @param float $frameRate 60 | * 61 | * @return VideoFormatInterface 62 | */ 63 | public function setFrameRate($frameRate); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/Exception/ExecutableNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Exception; 14 | 15 | use Exception; 16 | 17 | /** 18 | * Executable file not found. 19 | * 20 | * @package Arhitector\Transcoder\Exception 21 | */ 22 | class ExecutableNotFoundException extends TranscoderException 23 | { 24 | 25 | /** 26 | * @var string Executable file. 27 | */ 28 | protected $executable; 29 | 30 | /** 31 | * Construct the exception. 32 | * 33 | * @param string $message The Exception message to throw. 34 | * @param string $executableFile 35 | * @param Exception $previous The previous exception used for the exception chaining. 36 | */ 37 | public function __construct($message, $executableFile, Exception $previous = null) 38 | { 39 | $this->setExecutableFile($executableFile); 40 | 41 | parent::__construct($message, 0, $previous); 42 | } 43 | 44 | /** 45 | * Get binary value. 46 | * 47 | * @return string 48 | */ 49 | public function getExecutableFile() 50 | { 51 | return $this->executable; 52 | } 53 | 54 | /** 55 | * Sets the binary value. 56 | * 57 | * @param string $executableFile 58 | * 59 | * @return ExecutableNotFoundException 60 | */ 61 | protected function setExecutableFile($executableFile) 62 | { 63 | $this->executable = (string) $executableFile; 64 | 65 | return $this; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/Format/Flv.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Flv video format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Flv extends VideoFormat 23 | { 24 | 25 | /** 26 | * VideoFormat constructor. 27 | * 28 | * @param Codec|string $audioCodec 29 | * @param Codec|string $videoCodec 30 | * 31 | * @throws \InvalidArgumentException 32 | */ 33 | public function __construct($audioCodec = 'libmp3lame', $videoCodec = 'flv1') 34 | { 35 | parent::__construct($audioCodec, $videoCodec); 36 | 37 | $this->setExtensions(['flv']); 38 | $this->setAvailableVideoCodecs(['flv', 'flv1']); 39 | $this->setAvailableAudioCodecs(['libmp3lame', 'libshine', 'mp3', 'mp3pro', 'lame']); 40 | } 41 | 42 | /** 43 | * Set frequency value. 44 | * 45 | * @param int $frequency 46 | * 47 | * @return Flv 48 | * @throws \InvalidArgumentException 49 | */ 50 | public function setFrequency($frequency) 51 | { 52 | $frequencies = [44100, 22050, 11025]; 53 | 54 | if ( ! in_array($frequency, $frequencies, false)) 55 | { 56 | throw new \InvalidArgumentException(sprintf('Wrong sample rate value for %s, available values are %s', 57 | $frequency, implode(', ', $frequencies))); 58 | } 59 | 60 | parent::setFrequency($frequency); 61 | 62 | return $this; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /tests/Traits/OptionsAwareTraitTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests\Traits; 14 | 15 | use Arhitector\Transcoder\Traits\OptionsAwareTrait; 16 | use PHPUnit_Framework_Error; 17 | 18 | /** 19 | * Class OptionsAwareTraitTest. 20 | * 21 | * @package Arhitector\Transcoder\Tests\Traits 22 | */ 23 | class OptionsAwareTraitTest extends \PHPUnit_Framework_TestCase 24 | { 25 | 26 | /** 27 | * Test on successful. 28 | */ 29 | public function testSuccessful() 30 | { 31 | /** @var OptionsAwareTrait $mock */ 32 | $mock = $this->getMockForTrait(OptionsAwareTrait::class); 33 | $this->assertInstanceOf(get_class($mock), $mock->setOptions(['key' => 'value'])); 34 | $this->assertEquals(['key' => 'value'], $mock->getOptions()); 35 | } 36 | 37 | /** 38 | * Test on failure. 39 | * 40 | * @param mixed $value 41 | * @dataProvider dataProviderFailure 42 | */ 43 | public function testFailure($value) 44 | { 45 | $this->expectException(get_class(new PHPUnit_Framework_Error('', 0, '', 1))); 46 | 47 | /** @var OptionsAwareTrait $mock */ 48 | $mock = $this->getMockForTrait(OptionsAwareTrait::class); 49 | $mock->setOptions($value); 50 | } 51 | 52 | /** 53 | * The data provider. 54 | * 55 | * @return array 56 | */ 57 | public function dataProviderFailure() 58 | { 59 | return [ 60 | ['string'], 61 | [1234567890], 62 | [new \stdClass] 63 | ]; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/Traits/FilePathAwareTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Traits; 14 | 15 | use Arhitector\Transcoder\Exception\TranscoderException; 16 | 17 | /** 18 | * Class FilePathAware. 19 | * 20 | * @package Arhitector\Transcoder\Traits 21 | */ 22 | trait FilePathAwareTrait 23 | { 24 | 25 | /** 26 | * @var string The full path to the file. 27 | */ 28 | protected $filePath; 29 | 30 | /** 31 | * Get the full path to the file. 32 | * 33 | * @return string 34 | */ 35 | public function getFilePath() 36 | { 37 | return $this->filePath; 38 | } 39 | 40 | /** 41 | * Set file path. 42 | * 43 | * @param string $filePath 44 | * 45 | * @return $this 46 | * @throws \InvalidArgumentException 47 | * @throws \Arhitector\Transcoder\Exception\TranscoderException 48 | */ 49 | protected function setFilePath($filePath) 50 | { 51 | if ( ! is_string($filePath)) 52 | { 53 | throw new \InvalidArgumentException('File path must be a string type.'); 54 | } 55 | 56 | if (preg_match('~^(\w+:)?//~', $filePath) || is_link($filePath)) 57 | { 58 | throw new \InvalidArgumentException('File path must be a local path.'); 59 | } 60 | 61 | $filePath = realpath($filePath); 62 | 63 | if ( ! is_file($filePath)) 64 | { 65 | throw new TranscoderException('File path not found.'); 66 | } 67 | 68 | $this->filePath = $filePath; 69 | 70 | return $this; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/Format/Mkv.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Mkv video format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Mkv extends VideoFormat 23 | { 24 | 25 | /** 26 | * Format constructor. 27 | * 28 | * @param Codec|string $audioCodec 29 | * @param Codec|string $videoCodec 30 | * 31 | * @throws \InvalidArgumentException 32 | */ 33 | public function __construct($audioCodec = 'aac', $videoCodec = 'libtheora') 34 | { 35 | parent::__construct($audioCodec, $videoCodec); 36 | 37 | $this->setExtensions(['mkv']); 38 | $this->setAvailableVideoCodecs(['mpeg4', 'mpeg1', 'mpeg2', 'theora', 'libtheora', 'mpeg1video', 'mpeg2video']); 39 | $this->setAvailableAudioCodecs([ 40 | 'ac3', 41 | 'mp1', 42 | 'mp2', 43 | 'mp3', 44 | 'dts', 45 | 'tta', 46 | 'libvorbis', 47 | 'vorbis', 48 | 'flac', 49 | 'ra_144', 50 | 'libfdk_aac', 51 | 'libfaac', 52 | 'aac', 53 | 'libvo_aacenc', 54 | 'pcm_alaw', 55 | 'pcm_f32le', 56 | 'pcm_f64le', 57 | 'pcm_lxf', 58 | 'pcm_mulaw', 59 | 'pcm_s16le', 60 | 'pcm_s16le_planar', 61 | 'pcm_s24daud', 62 | 'pcm_s24le', 63 | 'pcm_s24le_planar', 64 | 'pcm_s32le', 65 | 'pcm_s32le_planar', 66 | 'pcm_s8', 67 | 'pcm_s8_planar', 68 | 'pcm_u16le', 69 | 'pcm_u24le', 70 | 'pcm_u32le', 71 | 'pcm_u8' 72 | ]); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/Stream/FrameStream.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | use Arhitector\Transcoder\TranscodeInterface; 16 | 17 | /** 18 | * Class FrameStream. 19 | * 20 | * @package Arhitector\Transcoder\Stream 21 | */ 22 | class FrameStream implements FrameStreamInterface 23 | { 24 | use StreamTrait; 25 | 26 | /** 27 | * @var int Width value. 28 | */ 29 | protected $width; 30 | 31 | /** 32 | * @var int Height value. 33 | */ 34 | protected $height; 35 | 36 | /** 37 | * Get width value. 38 | * 39 | * @return int 40 | */ 41 | public function getWidth() 42 | { 43 | return $this->width; 44 | } 45 | 46 | /** 47 | * Get height value. 48 | * 49 | * @return int 50 | */ 51 | public function getHeight() 52 | { 53 | return $this->height; 54 | } 55 | 56 | /** 57 | * Returns a bit mask of type. 58 | * 59 | * @return int 60 | */ 61 | public function getType() 62 | { 63 | return self::STREAM_FRAME; 64 | } 65 | 66 | /** 67 | * Set the width value. 68 | * 69 | * @param int $width 70 | * 71 | * @return FrameStream 72 | */ 73 | protected function setWidth($width) 74 | { 75 | $this->width = (int) $width; 76 | 77 | return $this; 78 | } 79 | 80 | /** 81 | * Set the height value. 82 | * 83 | * @param int $height 84 | * 85 | * @return FrameStream 86 | */ 87 | protected function setHeight($height) 88 | { 89 | $this->height = (int) $height; 90 | 91 | return $this; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/Format/FormatInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Event\EmitterInterface; 16 | use Arhitector\Transcoder\Preset\PresetInterface; 17 | use Arhitector\Transcoder\TimeInterval; 18 | 19 | /** 20 | * Interface FormatInterface. 21 | * 22 | * @package Arhitector\Transcoder\Format 23 | */ 24 | interface FormatInterface extends \ArrayAccess, EmitterInterface 25 | { 26 | 27 | /** 28 | * Returns a new format instance. 29 | * 30 | * @param array $options 31 | * 32 | * @return static 33 | */ 34 | public static function fromArray(array $options); 35 | 36 | /** 37 | * Get the duration value. 38 | * 39 | * @return TimeInterval 40 | */ 41 | public function getDuration(); 42 | 43 | /** 44 | * Gets the metadata. 45 | * 46 | * @return array 47 | */ 48 | public function getMetadata(); 49 | 50 | /** 51 | * Returns the number of passes. 52 | * 53 | * @return int 54 | */ 55 | public function getPasses(); 56 | 57 | /** 58 | * Get the format extensions. 59 | * 60 | * @return array 61 | */ 62 | public function getExtensions(); 63 | 64 | /** 65 | * Clone format instance with a new parameters from preset. 66 | * 67 | * @param PresetInterface $preset 68 | * 69 | * @return FormatInterface 70 | */ 71 | public function withPreset(PresetInterface $preset); 72 | 73 | /** 74 | * Gets the extra params. 75 | * 76 | * @return array 77 | */ 78 | public function getOptions(); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/Service/ServiceFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Service; 14 | 15 | use Arhitector\Transcoder\Traits\OptionsAwareTrait; 16 | 17 | /** 18 | * Class ServiceFactory. 19 | * 20 | * @package Arhitector\Transcoder\Service 21 | */ 22 | class ServiceFactory implements ServiceFactoryInterface 23 | { 24 | use OptionsAwareTrait; 25 | 26 | /** 27 | * ServiceFactory constructor. 28 | * 29 | * @param array $options 30 | */ 31 | public function __construct(array $options = []) 32 | { 33 | $this->setOptions($options); 34 | } 35 | 36 | /** 37 | * Get the decoder instance. 38 | * 39 | * @param array $options 40 | * 41 | * @return DecoderInterface 42 | */ 43 | public function getDecoderService(array $options = []) 44 | { 45 | /** @noinspection ExceptionsAnnotatingAndHandlingInspection */ 46 | return new Decoder($options ?: $this->getOptions()); 47 | } 48 | 49 | /** 50 | * Get the encoder instance. 51 | * 52 | * @param array $options 53 | * 54 | * @return EncoderInterface 55 | */ 56 | public function getEncoderService(array $options = []) 57 | { 58 | $options = $options ?: $this->getOptions(); 59 | 60 | if (isset($options[static::OPTION_USE_QUEUE]) && $options[static::OPTION_USE_QUEUE]) 61 | { 62 | /** @noinspection ExceptionsAnnotatingAndHandlingInspection */ 63 | return new EncoderQueue($this->options[static::OPTION_USE_QUEUE], $options); 64 | } 65 | 66 | /** @noinspection ExceptionsAnnotatingAndHandlingInspection */ 67 | return new Encoder($options); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/Format/Gif.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * The Gif picture format. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class Gif extends FrameFormat 23 | { 24 | 25 | /** 26 | * @var float Frame delay value. 27 | */ 28 | protected $frameDelay = .1; 29 | 30 | /** 31 | * @var int The loop count value. 32 | */ 33 | protected $loopCount = -1; 34 | 35 | /** 36 | * Format constructor. 37 | * 38 | * @param Codec|string $codec 39 | * 40 | * @throws \InvalidArgumentException 41 | */ 42 | public function __construct($codec = 'gif') 43 | { 44 | parent::__construct($codec); 45 | 46 | $this->setExtensions(['gif']); 47 | $this->setAvailableVideoCodecs(['gif']); 48 | } 49 | 50 | /** 51 | * Set frame delay value 52 | * 53 | * @param int|float $frame_delay 54 | * 55 | * @return Gif 56 | */ 57 | public function setFrameDelay($frame_delay) 58 | { 59 | // TODO 60 | } 61 | 62 | /** 63 | * Get frame delay value. 64 | * 65 | * @return float 66 | */ 67 | public function getFrameDelay() 68 | { 69 | return $this->frameDelay; 70 | } 71 | 72 | /** 73 | * Sets loop count value. 74 | * 75 | * @param int $loop_count 76 | * 77 | * @return Gif 78 | */ 79 | public function setLoopCount($loop_count) 80 | { 81 | // TODO 82 | } 83 | 84 | /** 85 | * Get the loop count value. 86 | * 87 | * @return int 88 | */ 89 | public function getLoopCount() 90 | { 91 | return $this->loopCount; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/Preset/FilePreset.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Preset; 14 | 15 | use Arhitector\Transcoder\Exception\TranscoderException; 16 | use Arhitector\Transcoder\Traits\FilePathAwareTrait; 17 | 18 | /** 19 | * Class FilePreset. 20 | * 21 | * @package Arhitector\Transcoder\Preset 22 | */ 23 | class FilePreset extends Preset 24 | { 25 | use FilePathAwareTrait; 26 | 27 | /** 28 | * @var string Delimiter character. 29 | */ 30 | protected $separator = '='; 31 | 32 | /** 33 | * FilePreset constructor. 34 | * 35 | * @param string $filePath 36 | * @param string $type 37 | * 38 | * @throws \Arhitector\Transcoder\Exception\TranscoderException 39 | * @throws \InvalidArgumentException 40 | */ 41 | public function __construct($filePath, $type = 'plain') 42 | { 43 | $this->setFilePath($filePath); 44 | 45 | if ( ! ($handle = fopen($this->getFilePath(), 'rb'))) 46 | { 47 | throw new TranscoderException('Unable to open preset file for reading.'); 48 | } 49 | 50 | $container = []; 51 | 52 | while ( ! feof($handle)) 53 | { 54 | $line = fgets($handle); 55 | 56 | if ( ! trim($line) || $line{0} == '#') 57 | { 58 | continue; 59 | } 60 | 61 | list($key, $value) = array_map('trim', explode($this->separator, $line, 2) + [1 => null]); 62 | 63 | if (array_key_exists($key, $container)) 64 | { 65 | if ( ! is_array($container[$key])) 66 | { 67 | $container[$key] = [$container[$key]]; 68 | } 69 | 70 | $container[$key][] = $value; 71 | } 72 | else 73 | { 74 | $container[$key] = $value; 75 | } 76 | } 77 | 78 | fclose($handle); 79 | parent::__construct($container); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/Service/ServiceFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Service; 14 | 15 | /** 16 | * Interface ServiceFactoryInterface. 17 | * 18 | * @package Arhitector\Transcoder\Service 19 | */ 20 | interface ServiceFactoryInterface 21 | { 22 | 23 | /** 24 | * @var string The path to the binary file 'ffmpeg'. 25 | */ 26 | const OPTION_FFMPEG_PATH = 'ffmpeg.path'; 27 | 28 | /** 29 | * @var string The option value alias for `ffmpeg.path`. 30 | */ 31 | const OPTION_FFMPEG_THREADS = 'ffmpeg.threads'; 32 | 33 | /** 34 | * @var string The option value alias for `ffprobe.path`. 35 | */ 36 | const OPTION_FFPROBE_PATH = 'ffprobe.path'; 37 | 38 | /** 39 | * @var string The option value alias for `timeout`. 40 | */ 41 | const OPTION_USE_TIMEOUT = 'timeout'; 42 | 43 | /** 44 | * @var string The option value alias for `use_queue`. 45 | */ 46 | const OPTION_USE_QUEUE = 'use_queue'; 47 | 48 | /** 49 | * @var string Check for codecs. 50 | */ 51 | const OPTION_TEST_CODECS = 'test_codecs'; 52 | 53 | /** 54 | * Gets the options. 55 | * 56 | * @return array 57 | */ 58 | public function getOptions(); 59 | 60 | /** 61 | * Sets the options value. 62 | * 63 | * @param array $options 64 | * 65 | * @return $this 66 | */ 67 | public function setOptions(array $options); 68 | 69 | /** 70 | * Get the decoder instance. 71 | * 72 | * @param array $options 73 | * 74 | * @return DecoderInterface 75 | */ 76 | public function getDecoderService(array $options = []); 77 | 78 | /** 79 | * Get the encoder instance. 80 | * 81 | * @param array $options 82 | * 83 | * @return EncoderInterface 84 | */ 85 | public function getEncoderService(array $options = []); 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/Exception/ExecutionFailureException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Exception; 14 | 15 | use Symfony\Component\Process\Process; 16 | use Exception; 17 | 18 | /** 19 | * Class ExecutionFailureException. 20 | * 21 | * @package Arhitector\Transcoder\Exception 22 | */ 23 | class ExecutionFailureException extends TranscoderException 24 | { 25 | 26 | /** 27 | * @var Process Process instance. 28 | */ 29 | protected $process; 30 | 31 | /** 32 | * Construct the exception. Note: The message is NOT binary safe. 33 | * 34 | * @param string $message The Exception message to throw. 35 | * @param Process $process Process instance. 36 | * @param int $code The Exception code. 37 | * @param Exception $previous The previous exception used for the exception chaining. Since 5.3.0 38 | * 39 | * @throws \Symfony\Component\Process\Exception\RuntimeException 40 | */ 41 | public function __construct($message, Process $process, $code = null, Exception $previous = null) 42 | { 43 | parent::__construct($message, (int) $process->getExitCode(), $previous); 44 | 45 | $this->setProcess($process); 46 | } 47 | 48 | /** 49 | * Wrapper for the command line. 50 | * 51 | * @return string 52 | */ 53 | public function getCommandLine() 54 | { 55 | return $this->getProcess() 56 | ->getCommandLine(); 57 | } 58 | 59 | /** 60 | * Get current process. 61 | * 62 | * @return Process 63 | */ 64 | public function getProcess() 65 | { 66 | return $this->process; 67 | } 68 | 69 | /** 70 | * Set process instance. 71 | * 72 | * @param Process $process 73 | * 74 | * @return ExecutionFailureException 75 | */ 76 | protected function setProcess(Process $process) 77 | { 78 | $this->process = $process; 79 | 80 | return $this; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/Stream/AudioStream.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | /** 16 | * Class AudioStream. 17 | * 18 | * @package Arhitector\Transcoder\Stream 19 | */ 20 | class AudioStream implements AudioStreamInterface 21 | { 22 | use StreamTrait; 23 | 24 | /** 25 | * @var int Audio channels value. 26 | */ 27 | protected $channels; 28 | 29 | /** 30 | * @var int Sample rate value. 31 | */ 32 | protected $frequency; 33 | 34 | /** 35 | * Get channels value. 36 | * 37 | * @return int 38 | */ 39 | public function getChannels() 40 | { 41 | return $this->channels; 42 | } 43 | 44 | /** 45 | * Get sample rate value. 46 | * 47 | * @return string 48 | */ 49 | public function getFrequency() 50 | { 51 | return $this->frequency; 52 | } 53 | 54 | /** 55 | * Returns a bit mask of type. 56 | * 57 | * @return int 58 | */ 59 | public function getType() 60 | { 61 | return self::STREAM_AUDIO; 62 | } 63 | 64 | /** 65 | * Sets the channels value. 66 | * 67 | * @param int $channels 68 | * 69 | * @return AudioStream 70 | * @throws \InvalidArgumentException 71 | */ 72 | protected function setChannels($channels) 73 | { 74 | if ( ! is_numeric($channels) || $channels < 1) 75 | { 76 | throw new \InvalidArgumentException('Wrong channels value.'); 77 | } 78 | 79 | $this->channels = $channels; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Set sample rate value. 86 | * 87 | * @param int $sampleRate 88 | * 89 | * @return AudioStream 90 | * @throws \InvalidArgumentException 91 | */ 92 | protected function setFrequency($sampleRate) 93 | { 94 | if ( ! is_numeric($sampleRate) || $sampleRate < 0) 95 | { 96 | throw new \InvalidArgumentException('Wrong sample rate value.'); 97 | } 98 | 99 | $this->frequency = (int) $sampleRate; 100 | 101 | return $this; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/Filter/Overlay.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\TranscodeInterface; 17 | 18 | /** 19 | * Overlay one video on top of another. 20 | * 21 | * @package Arhitector\Transcoder\Filter 22 | */ 23 | class Overlay extends FilterChain 24 | { 25 | 26 | protected $x = 0; 27 | 28 | protected $y = 0; 29 | 30 | /** 31 | * Overlay constructor. 32 | */ 33 | public function __construct() 34 | { 35 | 36 | } 37 | 38 | /** 39 | * Apply filter. 40 | * 41 | * @param TranscodeInterface $media 42 | * @param FormatInterface $format 43 | * 44 | * @return array 45 | */ 46 | public function apply(TranscodeInterface $media, FormatInterface $format) 47 | { 48 | $this->filters = new \SplPriorityQueue(); 49 | $this->addFilter(new SimpleFilter([ 50 | 'filter:v' => [ 51 | 'overlay' => urldecode(http_build_query([ 52 | 'x' => $this->getX(), 53 | 'y' => $this->getY(), 54 | ], null, ':')) 55 | ] 56 | ])); 57 | 58 | return parent::apply($media, $format); 59 | } 60 | 61 | /** 62 | * @return int 63 | */ 64 | public function getX() 65 | { 66 | return $this->x; 67 | } 68 | 69 | /** 70 | * Set the expression for the x coordinates of the overlaid video on the main video. 71 | * 72 | * @param int $x 73 | * 74 | * @return Overlay 75 | */ 76 | public function setX($x) 77 | { 78 | $this->x = $x; 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * @return int 85 | */ 86 | public function getY() 87 | { 88 | return $this->y; 89 | } 90 | 91 | /** 92 | * Set the expression for the y coordinates of the overlaid video on the main video. 93 | * 94 | * @param int $y 95 | * 96 | * @return Overlay 97 | */ 98 | public function setY($y) 99 | { 100 | $this->y = $y; 101 | 102 | return $this; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/Point.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Class Point. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | class Point 21 | { 22 | 23 | /** 24 | * @var int 25 | */ 26 | protected $x; 27 | 28 | /** 29 | * @var int 30 | */ 31 | protected $y; 32 | 33 | /** 34 | * Point constructor. 35 | * 36 | * @param int $x 37 | * @param int $y 38 | * 39 | * @throws \InvalidArgumentException 40 | */ 41 | public function __construct($x, $y) 42 | { 43 | $this->setX($x); 44 | $this->setY($y); 45 | } 46 | 47 | /** 48 | * Get X value. 49 | * 50 | * @return int 51 | */ 52 | public function getX() 53 | { 54 | return $this->x; 55 | } 56 | 57 | /** 58 | * Get Y value. 59 | * 60 | * @return int 61 | */ 62 | public function getY() 63 | { 64 | return $this->y; 65 | } 66 | 67 | /** 68 | * Return the array of coordinates. 69 | * 70 | * @return array 71 | */ 72 | public function toArray() 73 | { 74 | return [ 75 | 'x' => $this->getX(), 76 | 'y' => $this->getY() 77 | ]; 78 | } 79 | 80 | /** 81 | * Set X value. 82 | * 83 | * @param int $x 84 | * 85 | * @return Point 86 | * @throws \InvalidArgumentException 87 | */ 88 | protected function setX($x) 89 | { 90 | if ( ! is_numeric($x) || $x < 0) 91 | { 92 | throw new \InvalidArgumentException('The value of x must be a positive numeric.'); 93 | } 94 | 95 | $this->x = (int) $x; 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * Set Y value. 102 | * 103 | * @param int $y 104 | * 105 | * @return Point 106 | * @throws \InvalidArgumentException 107 | */ 108 | protected function setY($y) 109 | { 110 | if ( ! is_numeric($y) || $y < 0) 111 | { 112 | throw new \InvalidArgumentException('The value of y must be a positive numeric.'); 113 | } 114 | 115 | $this->y = (int) $y; 116 | 117 | return $this; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /tests/Traits/MetadataTraitTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests\Traits; 14 | 15 | use Arhitector\Transcoder\Traits\MetadataTrait; 16 | 17 | /** 18 | * Class MetadataTraitTest 19 | * 20 | * @package Arhitector\Transcoder\Tests\Traits 21 | */ 22 | class MetadataTraitTest extends \PHPUnit_Framework_TestCase 23 | { 24 | 25 | /** 26 | * Test of the container. 27 | */ 28 | public function testContainer() 29 | { 30 | /** @var MetadataTrait $mock */ 31 | $mock = $this->getObjectForTrait(MetadataTrait::class); 32 | 33 | $method = new \ReflectionMethod(get_class($mock), 'setMetadata'); 34 | $method->setAccessible(true); 35 | 36 | // empty container 37 | $this->assertEquals([], $mock->getMetadata()); 38 | 39 | // simple container 40 | $method->invoke($mock, ['key' => 'value']); 41 | $this->assertEquals(['key' => 'value'], $mock->getMetadata()); 42 | 43 | // only scalar values 44 | $value = new \StdClass; 45 | $method->invoke($mock, ['key' => $value, 'scalar' => true]); 46 | $this->assertEquals(['scalar' => true], $mock->getMetadata()); 47 | 48 | // key with value 49 | $value = mt_rand(); 50 | $method->invoke($mock, 'key', $value); 51 | $this->assertEquals(['key' => $value], $mock->getMetadata()); 52 | } 53 | 54 | /** 55 | * Methods: offsetExists, offsetGet, offsetSet, offsetUnset 56 | */ 57 | public function testArrayAccess() 58 | { 59 | $value = mt_rand(); 60 | 61 | /** @var MetadataTrait $mock */ 62 | $mock = $this->getObjectForTrait(MetadataTrait::class); 63 | 64 | // setter 65 | $this->assertEmpty($mock->getMetadata()); 66 | $mock->offsetSet($value, 'value'); 67 | 68 | // exists 69 | $this->assertTrue($mock->offsetExists($value)); 70 | $this->assertFalse($mock->offsetExists($value + 1)); 71 | 72 | // getter 73 | $this->assertEquals('value', $mock->offsetGet($value)); 74 | $this->assertNull($mock->offsetGet($value - 1)); 75 | 76 | // unset 77 | $mock->offsetUnset($value); 78 | $this->assertEmpty($mock->getMetadata()); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/Format/AudioFormatInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Interface AudioFormatInterface. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | interface AudioFormatInterface extends FormatInterface 23 | { 24 | 25 | /** 26 | * Gets the audio channels value. 27 | * 28 | * @return int 29 | */ 30 | public function getChannels(); 31 | 32 | /** 33 | * Sets the channels value. 34 | * 35 | * @param int $channels 36 | * 37 | * @return AudioFormatInterface 38 | * @throws \InvalidArgumentException 39 | */ 40 | public function setChannels($channels); 41 | 42 | /** 43 | * Get audio codec. 44 | * 45 | * @return Codec 46 | */ 47 | public function getAudioCodec(); 48 | 49 | /** 50 | * Sets the audio codec, Should be in the available ones, otherwise an exception is thrown. 51 | * 52 | * @param Codec $audioCodec 53 | * 54 | * @return AudioFormatInterface 55 | * @throws \InvalidArgumentException 56 | */ 57 | public function setAudioCodec(Codec $audioCodec); 58 | 59 | /** 60 | * Get the audio bitrate value. 61 | * 62 | * @return int 63 | */ 64 | public function getAudioBitrate(); 65 | 66 | /** 67 | * Sets the audio bitrate value. 68 | * 69 | * @param int $bitRate 70 | * 71 | * @return AudioFormatInterface 72 | * @throws \InvalidArgumentException 73 | */ 74 | public function setAudioBitrate($bitRate); 75 | 76 | /** 77 | * Get frequency value. 78 | * 79 | * @return int 80 | */ 81 | public function getFrequency(); 82 | 83 | /** 84 | * Set frequency value. 85 | * 86 | * @param int $frequency 87 | * 88 | * @return AudioFormatInterface 89 | * @throws \InvalidArgumentException 90 | */ 91 | public function setFrequency($frequency); 92 | 93 | /** 94 | * Get available codecs. 95 | * 96 | * @return string[] 97 | */ 98 | public function getAvailableAudioCodecs(); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/Filter/Graph.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use SplPriorityQueue; 16 | 17 | /** 18 | * Class Collection. 19 | * 20 | * @package Arhitector\Transcoder\Filter 21 | */ 22 | class Graph extends SplPriorityQueue 23 | { 24 | 25 | /** 26 | * @var int The counter sequence. 27 | */ 28 | protected $serial = PHP_INT_MAX; 29 | 30 | /** 31 | * Graph constructor. 32 | */ 33 | public function __construct() 34 | { 35 | 36 | } 37 | 38 | /** 39 | * Inserts an element in the queue by sifting it up. 40 | * 41 | * @param FilterInterface $filter 42 | * @param mixed $priority The associated priority. 43 | * 44 | * @return Graph 45 | * @throws \Arhitector\Transcoder\Exception\InvalidFilterException 46 | * @throws \InvalidArgumentException 47 | */ 48 | public function insert($filter, $priority) 49 | { 50 | if ( ! $filter instanceof FilterInterface) 51 | { 52 | throw new \InvalidArgumentException('The filter must be an instance of FilterInterface.'); 53 | } 54 | 55 | if ($filter instanceof FilterChainInterface) 56 | { 57 | parent::insert($filter, [-$priority, $this->serial--]); 58 | } 59 | else 60 | { 61 | if ($this->isEmpty()) 62 | { 63 | parent::insert(new FilterChain(), [0, $this->serial--]); 64 | } 65 | 66 | $this->top()->addFilter($filter, $priority); 67 | } 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * Return current node pointed by the iterator. 74 | * 75 | * @return FilterInterface 76 | */ 77 | public function current() 78 | { 79 | return parent::current(); 80 | } 81 | 82 | /** 83 | * Peeks at the node from the top of the queue. 84 | * 85 | * @return FilterChainInterface 86 | */ 87 | public function top() 88 | { 89 | return parent::top(); 90 | } 91 | 92 | /** 93 | * Extracts a node from top of the heap and sift up. 94 | * 95 | * @return FilterInterface 96 | */ 97 | public function extract() 98 | { 99 | return parent::extract(); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/Traits/MetadataTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Traits; 14 | 15 | /** 16 | * Class MetadataTrait. 17 | * 18 | * @package Arhitector\Transcoder\Traits 19 | */ 20 | trait MetadataTrait 21 | { 22 | 23 | /** 24 | * @var array The metadata tags or other. 25 | */ 26 | protected $metadata = []; 27 | 28 | /** 29 | * Gets the metadata. 30 | * 31 | * @return array 32 | */ 33 | public function getMetadata() 34 | { 35 | return $this->metadata; 36 | } 37 | 38 | /** 39 | * Check the metadata. 40 | * 41 | * @param mixed $tagName 42 | * 43 | * @return boolean 44 | */ 45 | public function offsetExists($tagName) 46 | { 47 | return array_key_exists($tagName, $this->metadata); 48 | } 49 | 50 | /** 51 | * Get metadata. 52 | * 53 | * @param mixed $tagName 54 | * 55 | * @return mixed 56 | * @throws \OutOfBoundsException 57 | */ 58 | public function offsetGet($tagName) 59 | { 60 | if ( ! $this->offsetExists($tagName)) 61 | { 62 | return null; 63 | } 64 | 65 | return $this->metadata[$tagName]; 66 | } 67 | 68 | /** 69 | * Sets the metadata. 70 | * 71 | * @param string $tagName 72 | * @param mixed $value The value to set. 73 | * 74 | * @return void 75 | */ 76 | public function offsetSet($tagName, $value) 77 | { 78 | $this->metadata[$tagName] = $value; 79 | } 80 | 81 | /** 82 | * Removes metadata. 83 | * 84 | * @param mixed $tagName 85 | * 86 | * @return void 87 | */ 88 | public function offsetUnset($tagName) 89 | { 90 | unset($this->metadata[$tagName]); 91 | } 92 | 93 | /** 94 | * Sets the metadata. 95 | * 96 | * @param string|array $metadata 97 | * @param mixed $value 98 | * 99 | * @return $this 100 | */ 101 | protected function setMetadata($metadata, $value = null) 102 | { 103 | if ( ! is_array($metadata)) 104 | { 105 | $metadata = [(string) $metadata => $value]; 106 | } 107 | 108 | $this->metadata = array_filter($metadata, 'is_scalar'); 109 | 110 | return $this; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /tests/Traits/FilePathAwareTraitTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests\Traits; 14 | 15 | use Arhitector\Transcoder\Traits\FilePathAwareTrait; 16 | 17 | /** 18 | * Class FilePathAwareTraitTest. 19 | * 20 | * @package Arhitector\Transcoder\Tests\Traits 21 | */ 22 | class FilePathAwareTraitTest extends \PHPUnit_Framework_TestCase 23 | { 24 | 25 | /** 26 | * @var FilePathAwareTrait 27 | */ 28 | protected $awareTrait; 29 | 30 | /** 31 | * The set up method. 32 | */ 33 | public function setUp() 34 | { 35 | $this->awareTrait = $this->getObjectForTrait(FilePathAwareTrait::class); 36 | } 37 | 38 | /** 39 | * Test on successful. 40 | */ 41 | public function testSuccessful() 42 | { 43 | $this->getReflection(__FILE__); 44 | $this->assertEquals($this->awareTrait->getFilePath(), __FILE__); 45 | } 46 | 47 | /** 48 | * Test on failure. 49 | * 50 | * @param mixed $value 51 | * 52 | * @dataProvider dataProviderFailure 53 | * @expectedException \InvalidArgumentException 54 | */ 55 | public function testFailure($value) 56 | { 57 | $this->getReflection($value); 58 | } 59 | 60 | /** 61 | * Test on failure if the file path not found. 62 | * 63 | * @expectedException \Arhitector\Transcoder\Exception\TranscoderException 64 | */ 65 | public function testFailureNotFound() 66 | { 67 | $this->getReflection(__FILE__.mt_rand()); 68 | } 69 | 70 | /** 71 | * The data provider. 72 | * 73 | * @return array 74 | */ 75 | public function dataProviderFailure() 76 | { 77 | return [ 78 | [new \stdClass], 79 | ['https://example.com/file.ext'] 80 | ]; 81 | } 82 | 83 | /** 84 | * Get the ReflectionMethod instance. 85 | * 86 | * @param string $filePath 87 | * 88 | * @return \ReflectionMethod 89 | */ 90 | protected function getReflection($filePath) 91 | { 92 | $reflection = new \ReflectionMethod($this->awareTrait, 'setFilePath'); 93 | $reflection->setAccessible(true); 94 | $reflection->invoke($this->awareTrait, $filePath); 95 | 96 | return $reflection; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/Service/EncoderQueue.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | 14 | namespace Arhitector\Transcoder\Service; 15 | 16 | use Arhitector\Transcoder\Format\FormatInterface; 17 | use Arhitector\Transcoder\TranscodeInterface; 18 | 19 | //use SimpleQueue\Job; 20 | //use SimpleQueue\QueueAdapterInterface as QueueInterface; 21 | 22 | /** 23 | * Class EncoderQueue. 24 | * 25 | * @package Arhitector\Jumper\Service 26 | */ 27 | class EncoderQueue extends Encoder 28 | { 29 | 30 | /** 31 | * @var QueueInterface 32 | */ 33 | // protected $queue; 34 | 35 | /** 36 | * Encoder constructor. 37 | * 38 | * @param QueueInterface $queue 39 | * @param array $options 40 | * 41 | * @throws \Arhitector\Transcoder\Exception\ExecutableNotFoundException 42 | */ 43 | // public function __construct(QueueInterface $queue, array $options = []) 44 | // { 45 | // parent::__construct($options); 46 | // 47 | // $this->queue = $queue; 48 | // } 49 | 50 | /** 51 | * Constructs and returns the iterator with instances of 'Process'. 52 | * 53 | * @param TranscodeInterface $media it may be a stream or media wrapper. 54 | * @param FormatInterface $format new format. 55 | * @param array $options 56 | * 57 | * @return \Iterator|\Symfony\Component\Process\Process[] returns the instances of 'Process'. 58 | * @throws \RuntimeException 59 | */ 60 | // public function transcoding(TranscodeInterface $media, FormatInterface $format, array $options = []) 61 | // { 62 | // $commandLines = []; 63 | // 64 | // foreach (parent::transcoding($media, $format, $options) as $pass => $process) 65 | // { 66 | // $commandLines[$pass] = $process->getCommandLine(); 67 | // 68 | // // fix: because Symfony uses private properties without setters methods :-((( 69 | // $property = new \ReflectionProperty($process, 'status'); 70 | // $property->setAccessible(true); 71 | // $property->setValue($process, $process::STATUS_TERMINATED); 72 | // 73 | // yield $pass => $process; 74 | // } 75 | // 76 | // if ($commandLines) 77 | // { 78 | // $this->queue->push(new Job(['transcoding', 'command_line' => $commandLines], 'transcoding')); 79 | // } 80 | // } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/Filter/Cut.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\TimeInterval; 17 | use Arhitector\Transcoder\TranscodeInterface; 18 | 19 | /** 20 | * Class Cut. 21 | * 22 | * @package Arhitector\Transcoder\Filter 23 | */ 24 | class Cut implements AudioFilterInterface, FrameFilterInterface 25 | { 26 | 27 | /** 28 | * @var TimeInterval 29 | */ 30 | protected $startTime; 31 | 32 | /** 33 | * @var TimeInterval 34 | */ 35 | protected $duration; 36 | 37 | /** 38 | * Cut constructor. 39 | * 40 | * @param TimeInterval|int $start 41 | * @param TimeInterval $duration 42 | * 43 | * @throws \InvalidArgumentException 44 | */ 45 | public function __construct($start, TimeInterval $duration = null) 46 | { 47 | if ( ! $start instanceof TimeInterval) 48 | { 49 | $start = new TimeInterval($start); 50 | } 51 | 52 | $this->startTime = $start; 53 | $this->duration = $duration; 54 | } 55 | 56 | /** 57 | * Apply filter. 58 | * 59 | * @param TranscodeInterface $media 60 | * @param FormatInterface $format 61 | * 62 | * @return array 63 | * @throws \InvalidArgumentException 64 | */ 65 | public function apply(TranscodeInterface $media, FormatInterface $format) 66 | { 67 | $options = [ 68 | 'seek_start' => (string) $this->getStartTime() 69 | ]; 70 | 71 | if ($this->getDuration() !== null) 72 | { 73 | if ($this->getDuration()->toSeconds() > $media->getDuration()) 74 | { 75 | throw new \InvalidArgumentException('The duration value exceeds the allowable value.'); 76 | } 77 | 78 | $options['seek_end'] = (string) $this->getDuration(); 79 | } 80 | 81 | return $options; 82 | } 83 | 84 | /** 85 | * Get the start time value. 86 | * 87 | * @return TimeInterval 88 | */ 89 | public function getStartTime() 90 | { 91 | return $this->startTime; 92 | } 93 | 94 | /** 95 | * Get the duration value. 96 | * 97 | * @return TimeInterval 98 | */ 99 | public function getDuration() 100 | { 101 | return $this->duration; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/Codec.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Class Codec. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | class Codec 21 | { 22 | 23 | /** 24 | * @var string codec code. 25 | */ 26 | protected $codec; 27 | 28 | /** 29 | * @var string codec name. 30 | */ 31 | protected $name; 32 | 33 | /** 34 | * Codec constructor. 35 | * 36 | * @param string $codec 37 | * @param string $codecName 38 | * 39 | * @throws \InvalidArgumentException 40 | */ 41 | public function __construct($codec, $codecName = '') 42 | { 43 | $this->setCode($codec); 44 | 45 | if ($codecName !== null) 46 | { 47 | $this->setName($codecName); 48 | } 49 | } 50 | 51 | /** 52 | * Get codec code. 53 | * 54 | * @return string 55 | */ 56 | public function getCode() 57 | { 58 | return (string) $this->codec; 59 | } 60 | 61 | /** 62 | * Get Codec name. 63 | * 64 | * @return string 65 | */ 66 | public function getName() 67 | { 68 | return (string) $this->name; 69 | } 70 | 71 | /** 72 | * The __toString method allows a class to decide how it will react when it is converted to a string. 73 | * 74 | * @return string 75 | */ 76 | public function __toString() 77 | { 78 | return $this->getCode(); 79 | } 80 | 81 | /** 82 | * Set codec code value. 83 | * 84 | * @param string $codec 85 | * 86 | * @return Codec 87 | * @throws \InvalidArgumentException 88 | */ 89 | protected function setCode($codec) 90 | { 91 | if (empty($codec) || ! is_string($codec)) 92 | { 93 | throw new \InvalidArgumentException('The codec value must be a string type.'); 94 | } 95 | 96 | $this->codec = $codec; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Set codec name value. 103 | * 104 | * @param string $name 105 | * 106 | * @return Codec 107 | * @throws \InvalidArgumentException 108 | */ 109 | protected function setName($name) 110 | { 111 | if ( ! is_string($name)) 112 | { 113 | throw new \InvalidArgumentException('The codec name value must be a string type.'); 114 | } 115 | 116 | $this->name = $name; 117 | 118 | return $this; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/Filter/AudioDelay.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\TranscodeInterface; 17 | 18 | /** 19 | * Delay one or more audio channels. 20 | * 21 | * @package Arhitector\Transcoder\Filter 22 | */ 23 | class AudioDelay implements AudioFilterInterface 24 | { 25 | 26 | /** 27 | * @var array The delays audio channels. 28 | */ 29 | protected $delays = []; 30 | 31 | /** 32 | * AudioDelay constructor. 33 | * 34 | * @param string[] ...$delays List of delays in milliseconds for each channel. 35 | * 36 | * 37 | * // Delay first channel by 1.5 seconds, the third channel by 0.5 seconds and leave the second channel. 38 | * $filter = new AudioDelay(1500, 0, 500); 39 | * 40 | * // or 41 | * 42 | * $filter = new AudioDelay([ 43 | * 1500, 44 | * 0, 45 | * 500 46 | * ]); 47 | * 48 | */ 49 | public function __construct(...$delays) 50 | { 51 | if (isset($delays[0]) && is_array($delays[0])) 52 | { 53 | $delays = $delays[0]; 54 | } 55 | 56 | $this->setDelays($delays); 57 | } 58 | 59 | /** 60 | * Apply filter. 61 | * 62 | * @param TranscodeInterface $media 63 | * @param FormatInterface $format 64 | * 65 | * @return array 66 | */ 67 | public function apply(TranscodeInterface $media, FormatInterface $format) 68 | { 69 | return [ 70 | 'filter:a' => [ 71 | 'adelay' => sprintf('delays=%s', implode('|', $this->getDelays())) 72 | ] 73 | ]; 74 | } 75 | 76 | /** 77 | * Get the delays values. 78 | * 79 | * @return array 80 | */ 81 | public function getDelays() 82 | { 83 | return $this->delays; 84 | } 85 | 86 | /** 87 | * Set list of delays in milliseconds for each channel. 88 | * 89 | * @param array $delays 90 | * 91 | * 92 | * // Delay first channel by 1.5 seconds, the third channel by 0.5 seconds and leave the second channel. 93 | * $filter->setDelays([ 94 | * 1500, 95 | * 0, 96 | * 500 97 | * ]); 98 | * 99 | * @return AudioDelay 100 | */ 101 | public function setDelays(array $delays) 102 | { 103 | $this->delays = array_filter($delays, 'is_scalar'); 104 | 105 | return $this; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/Preset/Preset.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Preset; 14 | 15 | /** 16 | * Class Preset. 17 | * 18 | * @package Arhitector\Transcoder\Preset 19 | */ 20 | class Preset implements PresetInterface, \ArrayAccess 21 | { 22 | 23 | /** 24 | * @var array The options container. 25 | */ 26 | protected $options; 27 | 28 | /** 29 | * Preset constructor. 30 | * 31 | * @param string[] $options 32 | * 33 | * @throws \InvalidArgumentException 34 | */ 35 | public function __construct(array $options) 36 | { 37 | foreach ($options as $key => $value) 38 | { 39 | $this->offsetSet($key, $value); 40 | } 41 | } 42 | 43 | /** 44 | * Get the options. 45 | * 46 | * @return array 47 | */ 48 | public function toArray() 49 | { 50 | return $this->options; 51 | } 52 | 53 | /** 54 | * Whether a offset exists 55 | * 56 | * @param mixed $offset An offset to check for. 57 | * 58 | * @return boolean true on success or false on failure. 59 | */ 60 | public function offsetExists($offset) 61 | { 62 | return array_key_exists($offset, $this->options); 63 | } 64 | 65 | /** 66 | * Offset to retrieve 67 | * 68 | * @param mixed $offset The offset to retrieve. 69 | * 70 | * @return mixed Can return all value types. 71 | */ 72 | public function offsetGet($offset) 73 | { 74 | if ($this->offsetExists($offset)) 75 | { 76 | return $this->options[$offset]; 77 | } 78 | 79 | return null; 80 | } 81 | 82 | /** 83 | * Offset to set 84 | * 85 | * @param mixed $offset The offset to assign the value to. 86 | * @param mixed $value The value to set. 87 | * 88 | * @return $this 89 | * @throws \InvalidArgumentException 90 | */ 91 | public function offsetSet($offset, $value) 92 | { 93 | if ( ! is_scalar($offset)) 94 | { 95 | throw new \InvalidArgumentException('Wrong offset value.'); 96 | } 97 | 98 | $this->options[$offset] = $value; 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * Offset to unset 105 | * 106 | * @param mixed $offset The offset to unset. 107 | * 108 | * @return $this 109 | */ 110 | public function offsetUnset($offset) 111 | { 112 | if ($this->offsetExists($offset)) 113 | { 114 | unset($this->options[$offset]); 115 | } 116 | 117 | return $this; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/Filter/Resize.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Dimension; 16 | use Arhitector\Transcoder\Format\FormatInterface; 17 | use Arhitector\Transcoder\Format\FrameFormatInterface; 18 | use Arhitector\Transcoder\TranscodeInterface; 19 | 20 | /** 21 | * Class Resize. 22 | * 23 | * @package Arhitector\Transcoder\Filter 24 | */ 25 | class Resize implements FrameFilterInterface 26 | { 27 | 28 | /** 29 | * @var int The width value. 30 | */ 31 | protected $width; 32 | 33 | /** 34 | * @var int The height value. 35 | */ 36 | protected $height; 37 | 38 | /** 39 | * Resize constructor. 40 | * 41 | * @throws \InvalidArgumentException 42 | */ 43 | public function __construct($width = null, $height = null) 44 | { 45 | if ( ! is_numeric($width) && ! is_numeric($height)) 46 | { 47 | throw new \InvalidArgumentException('Wrong parameters scaling.'); 48 | } 49 | 50 | $this->width = (int) $width; 51 | $this->height = (int) $height; 52 | } 53 | 54 | /** 55 | * Apply filter. 56 | * 57 | * @param TranscodeInterface $media 58 | * @param FormatInterface $format 59 | * 60 | * @return array 61 | * @throws \InvalidArgumentException 62 | */ 63 | public function apply(TranscodeInterface $media, FormatInterface $format) 64 | { 65 | if ( ! $format instanceof FrameFormatInterface) 66 | { 67 | throw new \InvalidArgumentException('The filter resize can be used only with the format of the frame.'); 68 | } 69 | 70 | $format = $media->getFormat(); 71 | $dimension = new Dimension($format->getWidth(), $format->getHeight()); 72 | 73 | if ($this->width && ! $this->height) // Resize to width 74 | { 75 | $this->height = $this->width / $dimension->getRatio(); 76 | } 77 | 78 | if ( ! $this->width && $this->height) // Resize to height 79 | { 80 | $this->width = $this->height * $dimension->getRatio(); 81 | } 82 | 83 | // If the dimensions are the same, there's no need to resize. 84 | if($dimension->getWidth() === $this->width && $dimension->getHeight() === $this->height) 85 | { 86 | return []; 87 | } 88 | 89 | return [ 90 | 'filter:v' => [ 91 | 'scale' => http_build_query([ 92 | 'w' => $this->width, 93 | 'h' => $this->height 94 | ], null, ':') 95 | ] 96 | ]; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/TranscodeInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | use Arhitector\Transcoder\Filter\FilterInterface; 17 | use Arhitector\Transcoder\Format\FormatInterface; 18 | use Arhitector\Transcoder\Service\ServiceFactoryInterface; 19 | use Arhitector\Transcoder\Stream\Collection; 20 | use Arhitector\Transcoder\Stream\EnumerationInterface; 21 | use Arhitector\Transcoder\Stream\StreamInterface; 22 | 23 | /** 24 | * Interface TranscoderInterface. 25 | * 26 | * @package Arhitector\Transcoder 27 | */ 28 | interface TranscodeInterface extends EnumerationInterface 29 | { 30 | 31 | /** 32 | * Get the full path to the file. 33 | * 34 | * @return string 35 | */ 36 | public function getFilePath(); 37 | 38 | /** 39 | * Get duration value. 40 | * 41 | * @return float 42 | */ 43 | public function getDuration(); 44 | 45 | /** 46 | * Get current format. 47 | * 48 | * @return FormatInterface 49 | */ 50 | public function getFormat(); 51 | 52 | /** 53 | * Get a list of streams. 54 | * 55 | * @param int|callable $filter 56 | * 57 | * @return Collection|StreamInterface[] 58 | */ 59 | public function getStreams($filter = null); 60 | 61 | /** 62 | * Transcoding. 63 | * 64 | * @param FormatInterface $format 65 | * @param string $filePath 66 | * @param bool $overwrite 67 | * 68 | * @return TranscodeInterface 69 | */ 70 | public function save(FormatInterface $format, $filePath, $overwrite = true); 71 | 72 | /** 73 | * Add a new filter. 74 | * 75 | * @param FilterInterface $filter 76 | * @param int $priority range 0-99. 77 | * 78 | * @return TranscodeInterface 79 | * @throws InvalidFilterException 80 | */ 81 | public function addFilter(FilterInterface $filter, $priority = 0); 82 | 83 | /** 84 | * Reset filters. 85 | * 86 | * @return TranscodeInterface 87 | */ 88 | public function withoutFilters(); 89 | 90 | /** 91 | * Get the service instance. 92 | * 93 | * @return ServiceFactoryInterface 94 | */ 95 | public function getService(); 96 | 97 | /** 98 | * Add a new stream. 99 | * 100 | * @param StreamInterface $stream 101 | * 102 | * @return TranscodeInterface 103 | */ 104 | public function addStream(StreamInterface $stream); 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/Filter/Crop.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Dimension; 16 | use Arhitector\Transcoder\Format\FormatInterface; 17 | use Arhitector\Transcoder\Point; 18 | use Arhitector\Transcoder\TranscodeInterface; 19 | 20 | /** 21 | * Crop the input frame to given dimensions. 22 | * 23 | * @package Arhitector\Transcoder\Filter 24 | */ 25 | class Crop implements FrameFilterInterface 26 | { 27 | 28 | /** 29 | * @var Point 30 | */ 31 | protected $point; 32 | 33 | /** 34 | * @var Dimension 35 | */ 36 | protected $dimension; 37 | 38 | /** 39 | * @var bool If set to TRUE will force the output display aspect ratio to be the same of the input. 40 | */ 41 | protected $keepAspect = false; 42 | 43 | /** 44 | * Crop constructor. 45 | * 46 | * @param Point $start 47 | * @param Dimension $dimension 48 | */ 49 | public function __construct(Point $start, Dimension $dimension) 50 | { 51 | $this->setPoint($start); 52 | $this->setDimension($dimension); 53 | } 54 | 55 | /** 56 | * Returns the point. 57 | * 58 | * @return Point 59 | */ 60 | public function getPoint() 61 | { 62 | return $this->point; 63 | } 64 | 65 | /** 66 | * Returns the dimension. 67 | * 68 | * @return \Arhitector\Transcoder\Dimension 69 | */ 70 | public function getDimension() 71 | { 72 | return $this->dimension; 73 | } 74 | 75 | /** 76 | * Apply filter. 77 | * 78 | * @param TranscodeInterface $media 79 | * @param FormatInterface $format 80 | * 81 | * @return array 82 | */ 83 | public function apply(TranscodeInterface $media, FormatInterface $format) 84 | { 85 | return [ 86 | 'filter:v' => [ 87 | 'crop' => http_build_query([ 88 | 'out_w' => $this->getDimension()->getWidth(), 89 | 'out_h' => $this->getDimension()->getHeight(), 90 | 'x' => $this->getPoint()->getX(), 91 | 'y' => $this->getPoint()->getX() 92 | ], null, ':') 93 | ] 94 | ]; 95 | } 96 | 97 | /** 98 | * Set the Point instance. 99 | * 100 | * @param Point $point 101 | * 102 | * @return Crop 103 | */ 104 | protected function setPoint(Point $point) 105 | { 106 | $this->point = $point; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Set the Dimension instance. 113 | * 114 | * @param Dimension $dimension 115 | * 116 | * @return Crop 117 | */ 118 | protected function setDimension(Dimension $dimension) 119 | { 120 | $this->dimension = $dimension; 121 | 122 | return $this; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /tests/Stream/StreamTraitTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests\Stream; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | use Arhitector\Transcoder\Stream\StreamTrait; 17 | use Arhitector\Transcoder\TranscodeInterface; 18 | use PHPUnit_Framework_Error; 19 | 20 | /** 21 | * Class StreamTraitTest. 22 | * 23 | * @package Arhitector\Transcoder\Tests\Stream 24 | */ 25 | class StreamTraitTest extends \PHPUnit_Framework_TestCase 26 | { 27 | 28 | /** 29 | * @var StreamTrait 30 | */ 31 | protected $awareTrait; 32 | 33 | public function setUp() 34 | { 35 | $this->awareTrait = $this->getObjectForTrait(StreamTrait::class); 36 | } 37 | 38 | public function testConstructor() 39 | { 40 | $reflection = new \ReflectionMethod(get_class($this->awareTrait), '__construct'); 41 | $this->assertTrue($reflection->isPrivate()); 42 | } 43 | 44 | /** 45 | * @dataProvider dataConstructSuccess 46 | * 47 | * @param mixed $value 48 | */ 49 | public function testConstructSuccess($value) 50 | { 51 | $reflection = new \ReflectionMethod(get_class($this->awareTrait), '__construct'); 52 | $reflection->setAccessible(true); 53 | $reflection->invoke($this->awareTrait, $value); 54 | } 55 | 56 | /** 57 | * @dataProvider dataConstructFailure 58 | * 59 | * @param mixed $value 60 | */ 61 | public function testConstructFailure($value) 62 | { 63 | $reflection = new \ReflectionMethod(get_class($this->awareTrait), '__construct'); 64 | $reflection->setAccessible(true); 65 | $this->expectException(get_class(new PHPUnit_Framework_Error('', 0, '', 1))); 66 | $reflection->invoke($this->awareTrait, $value); 67 | } 68 | 69 | public function testSetterGetterCodecSuccessful() 70 | { 71 | $codec = new Codec('codec string', 'codec name'); 72 | $this->awareTrait->setCodec($codec); 73 | 74 | $this->assertEquals($codec, $this->awareTrait->getCodec()); 75 | } 76 | 77 | public function testSetterGetterCodecFailure() 78 | { 79 | $this->expectException(get_class(new PHPUnit_Framework_Error('', 0, '', 1))); 80 | $this->awareTrait->setCodec('codec string'); 81 | } 82 | 83 | public function dataConstructSuccess() 84 | { 85 | $transcoderMock = $this->getMockBuilder(TranscodeInterface::class) 86 | ->getMock(); 87 | 88 | $transcoderMock->expects($this->any()) 89 | ->method('getFilePath') 90 | ->willReturn(__FILE__); 91 | 92 | return [ 93 | [$transcoderMock] 94 | ]; 95 | } 96 | 97 | public function dataConstructFailure() 98 | { 99 | return [ 100 | ['string'], 101 | [0.123456789], 102 | [new \stdClass] 103 | ]; 104 | } 105 | 106 | public function dataSettersGetters() 107 | { 108 | return [ 109 | 110 | ]; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/Dimension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Dimension, used for manipulating width and height couples. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | class Dimension 21 | { 22 | 23 | /** 24 | * Create instance Dimension from string. 25 | * 26 | * @param string $dimension For example, '100x200', '500:400', '320,180', '768;360', '200X100' 27 | * 28 | * 29 | * $dimension = Dimension::fromString('200X100'); 30 | * 31 | * 32 | * @return Dimension 33 | * @throws \InvalidArgumentException 34 | */ 35 | public static function fromString($dimension) 36 | { 37 | if ( ! preg_match('/(\d+)\s?[:xX,;]{1}\s?(\d+)/', $dimension, $matches)) 38 | { 39 | throw new \InvalidArgumentException('Dimension string has an unsupported format.'); 40 | } 41 | 42 | return new self($matches[1], $matches[2]); 43 | } 44 | 45 | /** 46 | * @var int 47 | */ 48 | protected $width; 49 | 50 | /** 51 | * @var int 52 | */ 53 | protected $height; 54 | 55 | /** 56 | * Dimension Constructor. 57 | * 58 | * @param int $width 59 | * @param int $height 60 | * 61 | * @throws \InvalidArgumentException when one of the parameters is invalid 62 | */ 63 | public function __construct($width, $height) 64 | { 65 | $this->setWidth($width); 66 | $this->setHeight($height); 67 | } 68 | 69 | /** 70 | * Returns width. 71 | * 72 | * @return int 73 | */ 74 | public function getWidth() 75 | { 76 | return $this->width; 77 | } 78 | 79 | /** 80 | * Returns height. 81 | * 82 | * @return int 83 | */ 84 | public function getHeight() 85 | { 86 | return $this->height; 87 | } 88 | 89 | /** 90 | * Get ratio. 91 | * 92 | * @return float 93 | */ 94 | public function getRatio() 95 | { 96 | return $this->getWidth() / $this->getHeight(); 97 | } 98 | 99 | /** 100 | * Set the width value. 101 | * 102 | * @param int $width 103 | * 104 | * @return Dimension 105 | * @throws \InvalidArgumentException 106 | */ 107 | protected function setWidth($width) 108 | { 109 | if ( ! is_numeric($width) || $width <= 0) 110 | { 111 | throw new \InvalidArgumentException('The width value should be positive integer.'); 112 | } 113 | 114 | $this->width = (int) $width; 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * Set the height value. 121 | * 122 | * @param int $height 123 | * 124 | * @return Dimension 125 | * @throws \InvalidArgumentException 126 | */ 127 | protected function setHeight($height) 128 | { 129 | if ( ! is_numeric($height) || $height <= 0) 130 | { 131 | throw new \InvalidArgumentException('The height value should be positive integer.'); 132 | } 133 | 134 | $this->height = (int) $height; 135 | 136 | return $this; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/Subtitle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | use Arhitector\Transcoder\Exception\TranscoderException; 17 | use Arhitector\Transcoder\Filter\FilterInterface; 18 | use Arhitector\Transcoder\Format\FormatInterface; 19 | use Arhitector\Transcoder\Format\SubtitleFormat; 20 | use Arhitector\Transcoder\Format\SubtitleFormatInterface; 21 | use Arhitector\Transcoder\Stream\Collection; 22 | use Arhitector\Transcoder\Stream\SubtitleStream; 23 | 24 | /** 25 | * Class Subtitle. 26 | * 27 | * @package Arhitector\Transcoder 28 | */ 29 | class Subtitle implements SubtitleInterface 30 | { 31 | use TranscodeTrait; 32 | 33 | /** 34 | * Get duration value. 35 | * 36 | * @return float 37 | */ 38 | public function getDuration() 39 | { 40 | return 0.0; 41 | } 42 | 43 | /** 44 | * Add a new filter. 45 | * 46 | * @param FilterInterface $filter 47 | * @param int $priority range 0-99. 48 | * 49 | * @return TranscodeInterface 50 | * @throws InvalidFilterException 51 | */ 52 | public function addFilter(FilterInterface $filter, $priority = 0) 53 | { 54 | throw new TranscoderException(sprintf('The "%s" wrapper unsuppored filers.', __CLASS__)); 55 | } 56 | 57 | /** 58 | * Initializing. 59 | * 60 | * @param \StdClass $demuxing 61 | * 62 | * @return void 63 | */ 64 | protected function initialize(\StdClass $demuxing) 65 | { 66 | /** @var SubtitleFormatInterface $format */ 67 | $format = $this->findFormatClass($demuxing->format['format'], SubtitleFormat::class); 68 | 69 | if ( ! is_subclass_of($format, SubtitleFormatInterface::class)) 70 | { 71 | throw new TranscoderException(sprintf('This format unsupported in the "%s" wrapper.', __CLASS__)); 72 | } 73 | 74 | $streams = new Collection(); 75 | 76 | foreach ($demuxing->streams as $number => $stream) 77 | { 78 | if (isset($stream['type']) && strtolower($stream['type']) == 'subtitle') 79 | { 80 | $streams[$number] = SubtitleStream::create($this, $stream); 81 | } 82 | } 83 | 84 | $this->setStreams($streams); 85 | 86 | $this->setFormat($format::fromArray(array_filter($demuxing->format, function ($value) { 87 | return $value !== null; 88 | }))); 89 | } 90 | 91 | /** 92 | * It supports the type of media. 93 | * 94 | * @return bool 95 | */ 96 | protected function isSupportedFileType() 97 | { 98 | return ! (stripos($this->getMimeType(), 'text/plain') !== 0); 99 | } 100 | 101 | /** 102 | * Checks is supported the encoding in format. 103 | * 104 | * @param FormatInterface $format 105 | * 106 | * @return bool 107 | */ 108 | protected function isSupportedFormat(FormatInterface $format) 109 | { 110 | return $format instanceof SubtitleFormatInterface; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/Filter/SimpleFilter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\TranscodeInterface; 17 | 18 | /** 19 | * Class SimpleFilter. 20 | * 21 | * @package Arhitector\Transcoder\Filter 22 | */ 23 | class SimpleFilter implements AudioFilterInterface, FrameFilterInterface, \ArrayAccess 24 | { 25 | 26 | /** 27 | * @var array List of parameters. 28 | */ 29 | protected $parameters = []; 30 | 31 | /** 32 | * SimpleFilter constructor. 33 | * 34 | * @param array $parameters 35 | */ 36 | public function __construct(array $parameters = []) 37 | { 38 | $this->setParameters($parameters); 39 | } 40 | 41 | /** 42 | * Set new array with parameters. 43 | * 44 | * @param array $parameters 45 | * 46 | * @return SimpleFilter 47 | */ 48 | public function setParameters(array $parameters) 49 | { 50 | $this->parameters = $parameters; 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Apply filter. 57 | * 58 | * @param TranscodeInterface $media 59 | * @param FormatInterface $format 60 | * 61 | * @return array 62 | */ 63 | public function apply(TranscodeInterface $media, FormatInterface $format) 64 | { 65 | return $this->toArray(); 66 | } 67 | 68 | /** 69 | * Get parameters. 70 | * 71 | * @return array 72 | */ 73 | public function toArray() 74 | { 75 | return $this->parameters; 76 | } 77 | 78 | /** 79 | * Whether a offset exists 80 | * 81 | * @param mixed $offset 82 | * 83 | * @return boolean 84 | */ 85 | public function offsetExists($offset) 86 | { 87 | return array_key_exists($offset, $this->parameters); 88 | } 89 | 90 | /** 91 | * Offset to retrieve 92 | * 93 | * @param mixed $offset 94 | * 95 | * @return mixed 96 | * @throws \OutOfBoundsException 97 | */ 98 | public function offsetGet($offset) 99 | { 100 | if ( ! $this->offsetExists($offset)) 101 | { 102 | throw new \OutOfBoundsException('Index invalid or out of range.'); 103 | } 104 | 105 | return $this->parameters[$offset]; 106 | } 107 | 108 | /** 109 | * Offset to set 110 | * 111 | * @param mixed $offset The offset to assign the value to. 112 | * @param mixed $value The value to set. 113 | * 114 | * @return SimpleFilter 115 | * @throws \InvalidArgumentException 116 | */ 117 | public function offsetSet($offset, $value) 118 | { 119 | if ( ! is_scalar($offset)) 120 | { 121 | throw new \InvalidArgumentException('The offset value must be a scalar.'); 122 | } 123 | 124 | return $this; 125 | } 126 | 127 | /** 128 | * Remove parameter. 129 | * 130 | * @param mixed $parameter 131 | * 132 | * @return SimpleFilter 133 | */ 134 | public function offsetUnset($parameter) 135 | { 136 | unset($this->parameters[$parameter]); 137 | 138 | return $this; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/Stream/StreamInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | use Arhitector\Transcoder\Format\FormatInterface; 17 | use Arhitector\Transcoder\TimeInterval; 18 | use Arhitector\Transcoder\TranscodeInterface; 19 | 20 | /** 21 | * Interface StreamInterface. 22 | * 23 | * @package Arhitector\Transcoder\Stream 24 | */ 25 | interface StreamInterface extends \ArrayAccess, EnumerationInterface 26 | { 27 | 28 | /** 29 | * Returns a new format instance. 30 | * 31 | * @param TranscodeInterface $media 32 | * @param array $options 33 | * 34 | * 35 | * array (size=8) 36 | * 'channels' => int 2 37 | * 'frequency' => int 44100 38 | * 'codec' => object(Arhitector\Transcoder\Codec)[13] 39 | * protected 'codec' => string 'mp3' (length=3) 40 | * protected 'name' => string 'MP3 (MPEG audio layer 3)' (length=24) 41 | * 'index' => int 1 42 | * 'profile' => string 'base' (length=4) 43 | * 'bitrate' => int 320000 44 | * 'start_time' => float 0.025057 45 | * 'duration' => float 208.53551 46 | * 47 | * 48 | * @return static 49 | * @throws \InvalidArgumentException 50 | */ 51 | public static function create(TranscodeInterface $media, array $options = []); 52 | 53 | /** 54 | * Get the full path to the file. 55 | * 56 | * @return string 57 | */ 58 | public function getFilePath(); 59 | 60 | /** 61 | * Get Codec instance or NULL. 62 | * 63 | * @return Codec|null 64 | */ 65 | public function getCodec(); 66 | 67 | /** 68 | * Set a new Codec instance. 69 | * 70 | * @param Codec $codec 71 | * 72 | * @return StreamInterface 73 | */ 74 | public function setCodec(Codec $codec); 75 | 76 | /** 77 | * Get stream index value. 78 | * 79 | * @return int 80 | */ 81 | public function getIndex(); 82 | 83 | /** 84 | * Set a new order. 85 | * 86 | * @param int $position 87 | * 88 | * @return StreamInterface 89 | */ 90 | public function setIndex($position); 91 | 92 | /** 93 | * Get profile value. 94 | * 95 | * @return string 96 | */ 97 | public function getProfile(); 98 | 99 | /** 100 | * Get bit rate value. 101 | * 102 | * @return int 103 | */ 104 | public function getBitrate(); 105 | 106 | /** 107 | * Get stream start time. 108 | * 109 | * @return float 110 | */ 111 | public function getStartTime(); 112 | 113 | /** 114 | * Get duration value. 115 | * 116 | * @return TimeInterval 117 | */ 118 | public function getDuration(); 119 | 120 | /** 121 | * Stream save. 122 | * 123 | * @param FormatInterface $format 124 | * @param string $filePath 125 | * @param bool $overwrite 126 | * 127 | * @return bool 128 | */ 129 | public function save(FormatInterface $format, $filePath, $overwrite = true); 130 | 131 | /** 132 | * Get array of values. 133 | * 134 | * @return array 135 | */ 136 | public function toArray(); 137 | 138 | /** 139 | * Returns a bit mask of type. 140 | * 141 | * @return int 142 | */ 143 | public function getType(); 144 | 145 | /** 146 | * Gets the metadata. 147 | * 148 | * @return array 149 | */ 150 | public function getMetadata(); 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/TimeInterval.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Class TimeInterval. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | class TimeInterval 21 | { 22 | 23 | /** 24 | * Create TimeInterval from string. 25 | * 26 | * @param string $string For example, 'hh:mm:ss:frame', 'hh:mm:ss,frame', 'hh:mm:ss.frame' 27 | * 28 | * @return TimeInterval 29 | * @throws \InvalidArgumentException 30 | */ 31 | public static function fromString($string) 32 | { 33 | if ( ! preg_match('/^(\d+):(\d+):(\d+)[:,\.]{1}(\d+)$/', $string, $matches)) 34 | { 35 | throw new \InvalidArgumentException('Time string has an unsupported format.'); 36 | } 37 | 38 | return new self($matches[1] * 3600 + $matches[2] + 60 + $matches[3] + ($matches[4] / 100)); 39 | } 40 | 41 | /** 42 | * Create TimeCode from frames. 43 | * 44 | * @param int $frames 45 | * @param float $fps 46 | * 47 | * @return TimeInterval 48 | * @throws \InvalidArgumentException 49 | */ 50 | public static function fromFrame($frames, $fps) 51 | { 52 | return new self($frames / $fps); 53 | } 54 | 55 | /** 56 | * @var int The timestamp. 57 | */ 58 | protected $timestamp = 0; 59 | 60 | /** 61 | * TimeCode constructor. 62 | * 63 | * @param int $seconds 64 | * 65 | * @throws \InvalidArgumentException 66 | */ 67 | public function __construct($seconds) 68 | { 69 | $this->setTimestamp($seconds); 70 | } 71 | 72 | /** 73 | * Get the hours value. 74 | * 75 | * @return int 76 | */ 77 | public function getHours() 78 | { 79 | return (int) gmdate('H', $this->toSeconds()); 80 | } 81 | 82 | /** 83 | * Get the minutes value. 84 | * 85 | * @return int 86 | */ 87 | public function getMinutes() 88 | { 89 | return (int) gmdate('i', $this->toSeconds()); 90 | } 91 | 92 | /** 93 | * Get the seconds value. 94 | * 95 | * @return int 96 | */ 97 | public function getSeconds() 98 | { 99 | return (int) gmdate('s', $this->toSeconds()); 100 | } 101 | 102 | /** 103 | * Get the frames value. 104 | * 105 | * @return int 106 | */ 107 | public function getFrames() 108 | { 109 | return round(100 * ($this->toSeconds() - floor($this->toSeconds()))); 110 | } 111 | 112 | /** 113 | * Returns the time in seconds. 114 | * 115 | * @return float 116 | */ 117 | public function toSeconds() 118 | { 119 | return $this->timestamp; 120 | } 121 | 122 | /** 123 | * Get time string in the ffmpeg format. 124 | * 125 | * @return string 126 | */ 127 | public function __toString() 128 | { 129 | $timestamp = $this->toSeconds(); 130 | 131 | return sprintf('%s.%02d', gmdate('H:i:s', $timestamp), round(100 * ($timestamp - floor($timestamp)))); 132 | } 133 | 134 | /** 135 | * Set the timestamp value. 136 | * 137 | * @param int|float $seconds 138 | * 139 | * @return TimeInterval 140 | * @throws \InvalidArgumentException 141 | */ 142 | protected function setTimestamp($seconds) 143 | { 144 | if ( ! is_numeric($seconds) || $seconds < 0) 145 | { 146 | throw new \InvalidArgumentException('The seconds value should be a positive integer.'); 147 | } 148 | 149 | $this->timestamp = (float) $seconds; 150 | 151 | return $this; 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/Filter/FilterChain.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | use Arhitector\Transcoder\Format\FormatInterface; 17 | use Arhitector\Transcoder\TranscodeInterface; 18 | 19 | /** 20 | * Class FilterChain. 21 | * 22 | * @package Arhitector\Transcoder\Filter 23 | */ 24 | class FilterChain implements FilterChainInterface 25 | { 26 | 27 | /** 28 | * @var \SplPriorityQueue|FilterInterface[] The collection of filters. 29 | */ 30 | protected $filters; 31 | 32 | /** 33 | * @var array List of inputs. 34 | */ 35 | protected $inputs = []; 36 | 37 | /** 38 | * @var array List of outputs. 39 | */ 40 | protected $outputs = []; 41 | 42 | /** 43 | * @var int The counter sequence. 44 | */ 45 | protected $serial = PHP_INT_MAX; 46 | 47 | /** 48 | * FilterChain constructor. 49 | * 50 | * @param FilterInterface[] ...$filters 51 | * 52 | * @throws InvalidFilterException 53 | * @throws \InvalidArgumentException 54 | */ 55 | public function __construct(FilterInterface ...$filters) 56 | { 57 | $this->filters = new \SplPriorityQueue(); 58 | 59 | foreach ($filters as $filter) 60 | { 61 | $this->addFilter($filter, 0); 62 | } 63 | } 64 | 65 | /** 66 | * Add a new filter. 67 | * 68 | * @param FilterInterface $filter 69 | * @param int $priority range 0-99. 70 | * 71 | * @return FilterChainInterface 72 | * @throws \InvalidArgumentException 73 | * @throws InvalidFilterException 74 | */ 75 | public function addFilter(FilterInterface $filter, $priority = 0) 76 | { 77 | $this->filters->insert($filter, [-$priority, $this->serial--]); 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Attach other chains as input. 84 | * 85 | * @param string $label 86 | * 87 | * @return FilterChainInterface 88 | */ 89 | public function addInputLabel($label) 90 | { 91 | $this->inputs[] = (string) $label; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Attach other chains as output. 98 | * 99 | * @param string $label 100 | * 101 | * @return FilterChainInterface 102 | */ 103 | public function addOutputLabel($label) 104 | { 105 | $this->outputs[] = (string) $label; 106 | 107 | return $this; 108 | } 109 | 110 | /** 111 | * Apply filter. 112 | * 113 | * @param TranscodeInterface $media 114 | * @param FormatInterface $format 115 | * 116 | * @return array 117 | */ 118 | public function apply(TranscodeInterface $media, FormatInterface $format) 119 | { 120 | $options = []; 121 | 122 | foreach (clone $this->filters as $filter) 123 | { 124 | foreach ($filter->apply($media, $format) as $option => $value) 125 | { 126 | if (stripos($option, 'filter') !== false) 127 | { 128 | $option = 'filter'; 129 | } 130 | 131 | $options[$option][] = $value; 132 | } 133 | } 134 | 135 | if (isset($options['filter'])) 136 | { 137 | $option = []; 138 | 139 | foreach (array_merge_recursive(...$options['filter']) as $filter => $value) 140 | { 141 | $option[] = $filter.'='.implode(', '.$filter.'=', (array) $value); 142 | } 143 | 144 | $options['filter'] = implode('', $this->inputs).implode(', ', $option).implode('', $this->outputs); 145 | } 146 | 147 | return $options; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/Format/FormatTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Event\EmitterTrait; 16 | use Arhitector\Transcoder\Preset\PresetInterface; 17 | use Arhitector\Transcoder\TimeInterval; 18 | use Arhitector\Transcoder\Traits\MetadataTrait; 19 | use Arhitector\Transcoder\Traits\OptionsAwareTrait; 20 | 21 | /** 22 | * Class FormatTrait. 23 | * 24 | * @package Arhitector\Transcoder\Format 25 | */ 26 | trait FormatTrait 27 | { 28 | use EmitterTrait, MetadataTrait, OptionsAwareTrait { 29 | OptionsAwareTrait::setOptions as protected; 30 | } 31 | 32 | /** 33 | * Returns a new format instance. 34 | * 35 | * @param array $options 36 | * 37 | * 38 | * array (size=6) 39 | * 'audio_codec' => object(Arhitector\Transcoder\Codec)[13] 40 | * protected 'codec' => string 'mp3' (length=3) 41 | * protected 'name' => string '' (length=0) 42 | * 'audio_bitrate' => int 256000 43 | * 'channels' => int 6 44 | * 'frequency' => int 44100 45 | * 'duration' => int 900 46 | * 'metadata' => array (size=2) 47 | * 'title' => string 'Title' (length=5) 48 | * 'artist' => string 'Artist name' (length=11) 49 | * 50 | * 51 | * @return static 52 | */ 53 | public static function fromArray(array $options) 54 | { 55 | $self = new static(); 56 | 57 | foreach ($options as $option => $value) 58 | { 59 | $parameter = str_replace('_', '', 'set'.ucwords($option, '_')); 60 | 61 | if (method_exists($self, $parameter)) 62 | { 63 | $self->{$parameter}($value); 64 | } 65 | } 66 | 67 | return $self; 68 | } 69 | 70 | /** 71 | * @var TimeInterval Duration value. 72 | */ 73 | protected $duration; 74 | 75 | /** 76 | * @var array List of extensions. 77 | */ 78 | protected $extensions = []; 79 | 80 | /** 81 | * Get the duration value. 82 | * 83 | * @return TimeInterval 84 | */ 85 | public function getDuration() 86 | { 87 | return $this->duration; 88 | } 89 | 90 | /** 91 | * Get the format extensions. 92 | * 93 | * @return array 94 | */ 95 | public function getExtensions() 96 | { 97 | return $this->extensions; 98 | } 99 | 100 | /** 101 | * Clone format instance with a new parameters from preset. 102 | * 103 | * @param PresetInterface $preset 104 | * 105 | * @return FormatInterface 106 | */ 107 | public function withPreset(PresetInterface $preset) 108 | { 109 | $self = clone $this; 110 | $self->setOptions($preset->toArray()); 111 | 112 | return $self; 113 | } 114 | 115 | /** 116 | * Set the duration value. 117 | * 118 | * @param TimeInterval|float $duration 119 | * 120 | * @return $this 121 | * @throws \InvalidArgumentException 122 | */ 123 | protected function setDuration($duration) 124 | { 125 | if (is_numeric($duration) && $duration >= 0) 126 | { 127 | $duration = new TimeInterval($duration); 128 | } 129 | 130 | if ( ! $duration instanceof TimeInterval) 131 | { 132 | throw new \InvalidArgumentException('The duration value must be a positive number value.'); 133 | } 134 | 135 | $this->duration = $duration; 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * Sets the extensions value. 142 | * 143 | * @param array $extensions 144 | * 145 | * @return array an array of values that have been set 146 | */ 147 | protected function setExtensions(array $extensions) 148 | { 149 | return $this->extensions = array_filter($extensions, 'is_scalar'); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /tests/PointTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests; 14 | 15 | use Arhitector\Transcoder\Point; 16 | 17 | /** 18 | * Class PointTest. 19 | * 20 | * @package Arhitector\Transcoder\Tests 21 | */ 22 | class PointTest extends \PHPUnit_Framework_TestCase 23 | { 24 | /** @noinspection MoreThanThreeArgumentsInspection */ 25 | 26 | /** 27 | * @dataProvider dataConstructorSuccessful 28 | * 29 | * @param int $x 30 | * @param int $y 31 | * @param null $expectedX 32 | * @param null $expectedY 33 | */ 34 | public function testConstructorSuccessFul($x, $y, $expectedX = null, $expectedY = null) 35 | { 36 | $point = new Point($x, $y); 37 | $this->assertEquals($expectedX ?: $x, $point->getX()); 38 | $this->assertEquals($expectedY ?: $y, $point->getY()); 39 | } 40 | 41 | public function dataConstructorSuccessful() 42 | { 43 | return [ 44 | [0, 0], 45 | [0, 100], 46 | [150, 0], 47 | ['150', '150'], 48 | ['1.6', 1.1, 1, 1] 49 | ]; 50 | } 51 | 52 | /** 53 | * @dataProvider dataConstructorFailure 54 | * 55 | * @param int $x 56 | * @param int $y 57 | */ 58 | public function testConstructorFailure($x, $y) 59 | { 60 | $this->expectException(\InvalidArgumentException::class); 61 | new Point($x, $y); 62 | } 63 | 64 | public function dataConstructorFailure() 65 | { 66 | return [ 67 | [-1, 0], 68 | ['string', 0], 69 | [['array'], 0], 70 | [(object) ['object'], 0], 71 | [0, -1], 72 | [0, 'string'], 73 | [0, ['array']], 74 | [0, (object) ['object']] 75 | ]; 76 | } 77 | 78 | /** 79 | * @dataProvider dataSettersFailure 80 | * 81 | * @param string $method 82 | * @param mixed $value 83 | */ 84 | public function testSettersFailure($method, $value) 85 | { 86 | $point = $this->getInstanceWithoutConstructor(); 87 | 88 | $methodReflection = new \ReflectionMethod(Point::class, $method); 89 | $methodReflection->setAccessible(true); 90 | 91 | $this->expectException(\InvalidArgumentException::class); 92 | $methodReflection->invoke($point, $value); 93 | } 94 | 95 | public function dataSettersFailure() 96 | { 97 | return [ 98 | ['setX', -1], 99 | ['setX', 'string'], 100 | ['setX', ['array']], 101 | ['setX', (object) ['object']], 102 | ['setY', -1], 103 | ['setY', 'string'], 104 | ['setY', ['array']], 105 | ['setY', (object) ['object']], 106 | ]; 107 | } 108 | 109 | /** @noinspection MoreThanThreeArgumentsInspection */ 110 | 111 | /** 112 | * @dataProvider dataSettersAndGettersSuccessful 113 | * 114 | * @param string $methodSetter 115 | * @param string $methodGetter 116 | * @param mixed $value 117 | * @param mixed $expectedValue 118 | */ 119 | public function testSettersAndGettersSuccessful($methodSetter, $methodGetter, $value, $expectedValue) 120 | { 121 | $point = $this->getInstanceWithoutConstructor(); 122 | 123 | $setterReflection = new \ReflectionMethod(Point::class, $methodSetter); 124 | $setterReflection->setAccessible(true); 125 | $setterReflection->invoke($point, $value); 126 | 127 | $getterReflection = new \ReflectionMethod(Point::class, $methodGetter); 128 | $getterReflection->setAccessible(true); 129 | 130 | $this->assertEquals($expectedValue, $getterReflection->invoke($point)); 131 | } 132 | 133 | public function dataSettersAndGettersSuccessful() 134 | { 135 | return [ 136 | ['setX', 'getX', 0, 0], 137 | ['setX', 'getX', '100', 100], 138 | ['setX', 'getX', 2.9, 2], 139 | ['setY', 'getY', 0, 0], 140 | ['setY', 'getY', '100', 100], 141 | ['setY', 'getY', 2.9, 2], 142 | ]; 143 | } 144 | 145 | /** 146 | * @return Point|object 147 | */ 148 | protected function getInstanceWithoutConstructor() 149 | { 150 | return (new \ReflectionClass(Point::class)) 151 | ->newInstanceWithoutConstructor(); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/Stream/Collection.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Stream; 14 | 15 | use Arhitector\Transcoder\Exception\TranscoderException; 16 | 17 | /** 18 | * Class Collection. 19 | * 20 | * @package Arhitector\Transcoder\Stream 21 | */ 22 | class Collection implements EnumerationInterface, \Iterator, \Countable, \ArrayAccess 23 | { 24 | 25 | /** 26 | * @var StreamInterface[] 27 | */ 28 | protected $streams = []; 29 | 30 | /** 31 | * @var int Current position 32 | */ 33 | private $position = 0; 34 | 35 | /** 36 | * Collection constructor. 37 | * 38 | * @param array $streams 39 | * 40 | * @throws \Arhitector\Transcoder\Exception\TranscoderException 41 | */ 42 | public function __construct(array $streams = []) 43 | { 44 | foreach ($streams as $stream) 45 | { 46 | if ( ! $stream instanceof StreamInterface) 47 | { 48 | throw new TranscoderException('Class instance must be instanceof "StreamInterface".'); 49 | } 50 | } 51 | 52 | $this->streams = $streams; 53 | } 54 | 55 | /** 56 | * Returns the first StreamInterface from the list. 57 | * 58 | * @return StreamInterface|null 59 | */ 60 | public function getFirst() 61 | { 62 | return reset($this->streams) ?: null; 63 | } 64 | 65 | /** 66 | * Return the current stream. 67 | * 68 | * @return StreamInterface 69 | */ 70 | public function current() 71 | { 72 | return $this->streams[$this->position]; 73 | } 74 | 75 | /** 76 | * Move forward to next element. 77 | * 78 | * @return Collection 79 | */ 80 | public function next() 81 | { 82 | $this->position++; 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * Return the key of the current element. 89 | * 90 | * @return int 91 | */ 92 | public function key() 93 | { 94 | return $this->position; 95 | } 96 | 97 | /** 98 | * Checks if current position is valid. 99 | * 100 | * @return boolean 101 | */ 102 | public function valid() 103 | { 104 | return isset($this->streams[$this->position]); 105 | } 106 | 107 | /** 108 | * Rewind the Iterator to the first element. 109 | * 110 | * @return Collection 111 | */ 112 | public function rewind() 113 | { 114 | $this->position = 0; 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * Count streams of an collection. 121 | * 122 | * @return int 123 | */ 124 | public function count() 125 | { 126 | return count($this->streams); 127 | } 128 | 129 | /** 130 | * Whether a index exists. 131 | * 132 | * @param int $index 133 | * 134 | * @return bool 135 | */ 136 | public function offsetExists($index) 137 | { 138 | return isset($this->streams[$index]); 139 | } 140 | 141 | /** 142 | * Offset to retrieve. 143 | * 144 | * @param int $index 145 | * 146 | * @return StreamInterface 147 | * @throws \OutOfBoundsException 148 | */ 149 | public function offsetGet($index) 150 | { 151 | if ( ! $this->offsetExists($index)) 152 | { 153 | throw new \OutOfBoundsException('Index invalid or out of range.'); 154 | } 155 | 156 | return $this->streams[$index]; 157 | } 158 | 159 | /** 160 | * Sets a new stream instance at a specified index. 161 | * 162 | * @param int $index The index being set. 163 | * @param StreamInterface $value The new value for the index. 164 | * 165 | * @return Collection 166 | * @throws TranscoderException 167 | */ 168 | public function offsetSet($index, $value) 169 | { 170 | if ( ! $value instanceof StreamInterface) 171 | { 172 | throw new TranscoderException(sprintf('The new value must be an instance of %s', StreamInterface::class)); 173 | } 174 | 175 | $this->streams[$index ?: $this->count()] = $value; 176 | 177 | return $this; 178 | } 179 | 180 | /** 181 | * Offset to unset stream instance. 182 | * 183 | * @param int $index 184 | * 185 | * @return Collection 186 | */ 187 | public function offsetUnset($index) 188 | { 189 | if ($this->offsetExists($index)) 190 | { 191 | unset($this->streams[$index]); 192 | } 193 | 194 | return $this; 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /tests/DimensionTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests; 14 | 15 | use Arhitector\Transcoder\Dimension; 16 | use ReflectionClass; 17 | use ReflectionMethod; 18 | 19 | /** 20 | * Class DimensionTest 21 | * 22 | * @package Arhitector\Transcoder\Tests 23 | */ 24 | class DimensionTest extends \PHPUnit_Framework_TestCase 25 | { 26 | 27 | /** 28 | * @dataProvider dataConstructorSuccessful 29 | * 30 | * @param mixed $width 31 | * @param mixed $height 32 | * @param mixed $expWidth 33 | * @param mixed $expHeight 34 | */ 35 | public function testConstructorSuccessFul($width, $height, $expWidth, $expHeight) 36 | { 37 | $dimension = new Dimension($width, $height); 38 | 39 | $this->assertEquals($expWidth, $dimension->getWidth()); 40 | $this->assertEquals($expHeight, $dimension->getHeight()); 41 | } 42 | 43 | /** 44 | * The data provider for `testConstructorSuccessFul` 45 | * 46 | * @return array 47 | */ 48 | public function dataConstructorSuccessful() 49 | { 50 | return [ 51 | [100, 200, 100, 200], 52 | ['100', '200', 100, 200], 53 | [100.5, 200.7, 100, 200] 54 | ]; 55 | } 56 | 57 | /** 58 | * @dataProvider dataSettersAndGettersSuccessful 59 | * 60 | * @param mixed $value 61 | * @param mixed $expected 62 | */ 63 | public function testSettersAndGettersWidthSuccessful($value, $expected) 64 | { 65 | $dimension = $this->getInstanceWithoutConstructor(); 66 | 67 | $methodReflection = new ReflectionMethod(Dimension::class, 'setWidth'); 68 | $methodReflection->setAccessible(true); 69 | $methodReflection->invoke($dimension, $value); 70 | 71 | $this->assertEquals($expected, $dimension->getWidth()); 72 | } 73 | 74 | /** 75 | * @dataProvider dataSettersAndGettersSuccessful 76 | * 77 | * @param mixed $value 78 | * @param mixed $expected 79 | */ 80 | public function testSettersAndGettersHeightSuccessful($value, $expected) 81 | { 82 | $dimension = $this->getInstanceWithoutConstructor(); 83 | 84 | $methodReflection = new ReflectionMethod(Dimension::class, 'setHeight'); 85 | $methodReflection->setAccessible(true); 86 | $methodReflection->invoke($dimension, $value); 87 | 88 | $this->assertEquals($expected, $dimension->getHeight()); 89 | } 90 | 91 | /** 92 | * The data provider for setters and getters. 93 | * 94 | * @return array 95 | */ 96 | public function dataSettersAndGettersSuccessful() 97 | { 98 | return [ 99 | [100, 100], 100 | ['123', 123], 101 | [321.789, 321] 102 | ]; 103 | } 104 | 105 | /** 106 | * @dataProvider dataFromStringSuccessful 107 | * 108 | * @param string $value 109 | * @param int $width 110 | * @param int $height 111 | */ 112 | public function testFromStringSuccessful($value, $width, $height) 113 | { 114 | $method = new ReflectionMethod(Dimension::class, 'fromString'); 115 | $method->setAccessible(true); 116 | 117 | $dimension = $method->invoke(null, $value); 118 | 119 | $this->assertInstanceOf(Dimension::class, $dimension); 120 | $this->assertEquals($width, $dimension->getWidth()); 121 | $this->assertEquals($height, $dimension->getHeight()); 122 | } 123 | 124 | /** 125 | * The data provider for `testFromStringSuccessful` 126 | * 127 | * @return array 128 | */ 129 | public function dataFromStringSuccessful() 130 | { 131 | return [ 132 | ['100x200', 100, 200], 133 | ['500:400', 500, 400], 134 | ['320,180', 320, 180], 135 | ['768;360', 768, 360], 136 | ['200X100', 200, 100] 137 | ]; 138 | } 139 | 140 | /** 141 | * Test for `getRation` method. 142 | */ 143 | public function testRatio() 144 | { 145 | $this->assertEquals(1.3333333333333333, (new Dimension(320, 240))->getRatio()); 146 | $this->assertNotEquals(1.33, (new Dimension(320, 240))->getRatio()); 147 | $this->assertNotEquals('123', (new Dimension(100, 100))->getRatio()); 148 | } 149 | 150 | /** 151 | * @return Dimension|object 152 | */ 153 | protected function getInstanceWithoutConstructor() 154 | { 155 | return (new ReflectionClass(Dimension::class))->newInstanceWithoutConstructor(); 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/Format/FrameFormat.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | use Arhitector\Transcoder\TimeInterval; 17 | 18 | /** 19 | * Class FrameFormat. 20 | * 21 | * @package Arhitector\Transcoder\Format 22 | */ 23 | class FrameFormat implements FrameFormatInterface 24 | { 25 | use FormatTrait; 26 | 27 | /** 28 | * @var Codec The video codec value. 29 | */ 30 | protected $videoCodec; 31 | 32 | /** 33 | * @var int The width value. 34 | */ 35 | protected $width; 36 | 37 | /** 38 | * @var int The height value. 39 | */ 40 | protected $height; 41 | 42 | /** 43 | * @var string[] The list of available video codecs. 44 | */ 45 | protected $videoAvailableCodecs = []; 46 | 47 | /** 48 | * Format constructor. 49 | * 50 | * @param Codec|string $codec 51 | * 52 | * @throws \InvalidArgumentException 53 | */ 54 | public function __construct($codec = null) 55 | { 56 | if ($codec !== null) 57 | { 58 | if ( ! $codec instanceof Codec) 59 | { 60 | $codec = new Codec($codec); 61 | } 62 | 63 | $this->setVideoCodec($codec); 64 | } 65 | 66 | if ( ! $this->getDuration()) 67 | { 68 | $this->setDuration(new TimeInterval(0)); 69 | } 70 | } 71 | 72 | /** 73 | * Get width value. 74 | * 75 | * @return int 76 | */ 77 | public function getWidth() 78 | { 79 | return $this->width; 80 | } 81 | 82 | /** 83 | * Get height value. 84 | * 85 | * @return int 86 | */ 87 | public function getHeight() 88 | { 89 | return $this->height; 90 | } 91 | 92 | /** 93 | * Returns the number of passes. 94 | * 95 | * @return int 96 | */ 97 | public function getPasses() 98 | { 99 | return 1; 100 | } 101 | 102 | /** 103 | * Get the video/frame codec. 104 | * 105 | * @return Codec 106 | */ 107 | public function getVideoCodec() 108 | { 109 | return $this->videoCodec; 110 | } 111 | 112 | /** 113 | * Sets the video/frame codec, should be in the available ones, otherwise an exception is thrown. 114 | * 115 | * @param Codec $codec 116 | * 117 | * @return FrameFormat 118 | * @throws \InvalidArgumentException 119 | */ 120 | public function setVideoCodec(Codec $codec) 121 | { 122 | if ($this->getAvailableVideoCodecs() && ! in_array($codec, $this->getAvailableVideoCodecs(), false)) 123 | { 124 | throw new \InvalidArgumentException(sprintf('Wrong video codec value for "%s", available values are %s', 125 | $codec, implode(', ', $this->getAvailableVideoCodecs()))); 126 | } 127 | 128 | $this->videoCodec = $codec; 129 | 130 | return $this; 131 | } 132 | 133 | /** 134 | * Get available codecs. 135 | * 136 | * @return string[] 137 | */ 138 | public function getAvailableVideoCodecs() 139 | { 140 | return $this->videoAvailableCodecs; 141 | } 142 | 143 | /** 144 | * Set the width value. 145 | * 146 | * @param int $width 147 | * 148 | * @return FrameFormat 149 | * @throws \InvalidArgumentException 150 | */ 151 | protected function setWidth($width) 152 | { 153 | if ( ! is_numeric($width) || $width < 1) 154 | { 155 | throw new \InvalidArgumentException('Wrong the width value.'); 156 | } 157 | 158 | $this->width = $width; 159 | 160 | return $this; 161 | } 162 | 163 | /** 164 | * Set the height value. 165 | * 166 | * @param int $height 167 | * 168 | * @return FrameFormat 169 | * @throws \InvalidArgumentException 170 | */ 171 | protected function setHeight($height) 172 | { 173 | if ( ! is_numeric($height) || $height < 1) 174 | { 175 | throw new \InvalidArgumentException('Wrong the height value.'); 176 | } 177 | 178 | $this->height = $height; 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * Sets the list of available audio codecs. 185 | * 186 | * @param array $codecs 187 | * @param bool $force 188 | * 189 | * @return \Arhitector\Transcoder\Format\FrameFormat 190 | */ 191 | protected function setAvailableVideoCodecs(array $codecs, $force = false) 192 | { 193 | if ( ! $force && $this->getAvailableVideoCodecs()) 194 | { 195 | $codecs = array_intersect($this->getAvailableVideoCodecs(), $codecs); 196 | } 197 | 198 | $this->videoAvailableCodecs = array_map('strval', $codecs); 199 | 200 | return $this; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/Filter/Text.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Filter; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use Arhitector\Transcoder\Format\FrameFormatInterface; 17 | use Arhitector\Transcoder\Point; 18 | use Arhitector\Transcoder\Traits\ConvertEncodingTrait; 19 | use Arhitector\Transcoder\TranscodeInterface; 20 | 21 | /** 22 | * Draw a text string or text from a specified file on top of a video. 23 | * 24 | * @package Arhitector\Transcoder\Filter 25 | */ 26 | class Text implements FrameFilterInterface 27 | { 28 | use ConvertEncodingTrait; 29 | 30 | /** 31 | * @var int The font size to be used for drawing text. 32 | */ 33 | protected $size = 16; 34 | 35 | /** 36 | * @var string The text string to be drawn. 37 | */ 38 | protected $content; 39 | 40 | /** 41 | * @var string The color to be used for drawing fonts. 42 | */ 43 | protected $color = 'black'; 44 | 45 | /** 46 | * @var array The offsets where text will be drawn within the video frame. 47 | */ 48 | protected $position = [ 49 | 'x' => 0, 50 | 'y' => 0 51 | ]; 52 | 53 | /** 54 | * Text constructor. 55 | * 56 | * @param string $content 57 | */ 58 | public function __construct($content = null) 59 | { 60 | if ($content !== null) 61 | { 62 | $this->setContent($content); 63 | } 64 | } 65 | 66 | /** 67 | * Returns the font size. 68 | * 69 | * @return int 70 | */ 71 | public function getSize() 72 | { 73 | return $this->size; 74 | } 75 | 76 | /** 77 | * Sets the font size to be used for drawing text. 78 | * 79 | * @param int $size 80 | * 81 | * @return Text 82 | */ 83 | public function setSize($size) 84 | { 85 | $this->size = (int) $size; 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * Returns the text string. 92 | * 93 | * @return string 94 | */ 95 | public function getContent() 96 | { 97 | return $this->content; 98 | } 99 | 100 | /** 101 | * Sets the text string to be drawn. 102 | * 103 | * @param string $content 104 | * 105 | * @return Text 106 | */ 107 | public function setContent($content) 108 | { 109 | $this->content = $content; 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * Sets the color to be used for drawing fonts. 116 | * 117 | * @param string $color 118 | * 119 | * @return Text 120 | */ 121 | public function setColor($color) 122 | { 123 | $this->color = (string) $color; 124 | 125 | return $this; 126 | } 127 | 128 | /** 129 | * Get the color value. 130 | * 131 | * @return string 132 | */ 133 | public function getColor() 134 | { 135 | return $this->color; 136 | } 137 | 138 | /** 139 | * Sets the offsets where text will be drawn within the video frame. 140 | * 141 | * @param Point $point 142 | * 143 | * @return Text 144 | */ 145 | public function setPosition(Point $point) 146 | { 147 | $this->position = $point->toArray(); 148 | 149 | return $this; 150 | } 151 | 152 | /** 153 | * Sets the expressions which specify the offsets where text will be drawn within the video frame. 154 | * 155 | * @param string $xCoord 156 | * @param string $yCoord 157 | * 158 | * @return Text 159 | */ 160 | public function setPositionExpression($xCoord, $yCoord) 161 | { 162 | $this->position = [ 163 | 'x' => (string) $xCoord, 164 | 'y' => (string) $yCoord 165 | ]; 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * Returns the expressions. 172 | * 173 | * @return array 174 | */ 175 | public function getPosition() 176 | { 177 | return $this->position; 178 | } 179 | 180 | /** 181 | * Apply filter. 182 | * 183 | * @param TranscodeInterface $media 184 | * @param FormatInterface $format 185 | * 186 | * @return array 187 | */ 188 | public function apply(TranscodeInterface $media, FormatInterface $format) 189 | { 190 | if ( ! $format instanceof FrameFormatInterface) 191 | { 192 | throw new \InvalidArgumentException('The filter text can be used only with the format of the frame.'); 193 | } 194 | 195 | return [ 196 | 'filter:v' => [ 197 | 'drawtext' => urldecode(http_build_query([ 198 | 'text' => $this->convertEncoding($this->getContent()), 199 | 'fontsize' => $this->getSize(), 200 | 'fontcolor' => $this->getColor(), 201 | ] + $this->getPosition(), null, ':')) 202 | ] 203 | ]; 204 | } 205 | 206 | } -------------------------------------------------------------------------------- /tests/CodecTest.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Tests; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class CodecTest. 19 | * 20 | * @package Arhitector\Transcoder\Tests 21 | */ 22 | class CodecTest extends \PHPUnit_Framework_TestCase 23 | { 24 | 25 | /** 26 | * Testing constructor on successful. 27 | * 28 | * @dataProvider dataValidConstructor 29 | * 30 | * @param mixed $code 31 | * @param mixed $name 32 | */ 33 | public function testConstructorSuccessful($code, $name) 34 | { 35 | $codec = new Codec($code); 36 | $this->assertEquals($code, $codec->getCode()); 37 | 38 | $codec = new Codec($code, $name); 39 | $this->assertEquals($code, $codec->getCode()); 40 | $this->assertEquals($name, $codec->getName()); 41 | } 42 | 43 | public function dataValidConstructor() 44 | { 45 | return [ 46 | ['codec value', null], 47 | ['codec value', 'name'], 48 | ['codec value', ''] 49 | ]; 50 | } 51 | 52 | /** 53 | * Testing constructor on failure. 54 | * 55 | * @dataProvider dataInvalidConstructor 56 | * 57 | * @param mixed $code 58 | * @param mixed $name 59 | */ 60 | public function testConstructorFailure($code, $name) 61 | { 62 | $this->expectException(\InvalidArgumentException::class); 63 | new Codec($code, $name); 64 | } 65 | 66 | public function dataInvalidConstructor() 67 | { 68 | return [ 69 | // for 'code' 70 | ['', 'valid value'], 71 | [null, 'valid value'], 72 | [true, 'valid value'], 73 | [false, 'valid value'], 74 | [1, 'valid value'], 75 | [1.1, 'valid value'], 76 | [['array'], 'valid value'], 77 | [(object) ['object' => true], 'valid value'], 78 | 79 | // for 'name' 80 | ['valid value', true], 81 | ['valid value', false], 82 | ['valid value', 1], 83 | ['valid value', 1.1], 84 | ['valid value', ['array']], 85 | ['valid value', (object) ['object' => true]] 86 | ]; 87 | } 88 | 89 | public function testCanSerializeToString() 90 | { 91 | $expected = 'abcdefghijklmnopqrstuvwxyz'; 92 | $codec = new Codec($expected); 93 | $this->assertEquals($expected, $codec); 94 | } 95 | 96 | /** 97 | * @dataProvider dataSettersFailure 98 | * 99 | * @param string $setter 100 | * @param mixed $value 101 | */ 102 | public function testSettersFailure($setter, $value) 103 | { 104 | $codec = $this->getInstanceWithoutConstructor(); 105 | 106 | $setterReflection = new \ReflectionMethod(Codec::class, $setter); 107 | $setterReflection->setAccessible(true); 108 | 109 | $this->expectException(\InvalidArgumentException::class); 110 | $setterReflection->invoke($codec, $value); 111 | } 112 | 113 | public function dataSettersFailure() 114 | { 115 | return [ 116 | ['setCode', ''], 117 | ['setCode', null], 118 | ['setCode', true], 119 | ['setCode', false], 120 | ['setCode', 1], 121 | ['setCode', 1.1], 122 | ['setCode', ['array']], 123 | ['setCode', (object) ['object' => true]], 124 | ['setName', true], 125 | ['setName', false], 126 | ['setName', 1], 127 | ['setName', 1.1], 128 | ['setName', ['array']], 129 | ['setName', (object) ['object' => true]] 130 | ]; 131 | } 132 | 133 | /** 134 | * @dataProvider dataSettersAndGettersSuccessful 135 | * 136 | * @param string $setter 137 | * @param string $getter 138 | * @param mixed $value 139 | */ 140 | public function testSettersAndGettersSuccessful($setter, $getter, $value) 141 | { 142 | $codec = $this->getInstanceWithoutConstructor(); 143 | 144 | $setterReflection = new \ReflectionMethod(Codec::class, $setter); 145 | $setterReflection->setAccessible(true); 146 | $setterReflection->invoke($codec, $value); 147 | 148 | $getterReflection = new \ReflectionMethod(Codec::class, $getter); 149 | $getterReflection->setAccessible(true); 150 | 151 | $this->assertEquals($value, $getterReflection->invoke($codec)); 152 | } 153 | 154 | public function dataSettersAndGettersSuccessful() 155 | { 156 | return [ 157 | ['setCode', 'getCode', 'valid value'], 158 | ['setName', 'getName', 'valid value'], 159 | ['setName', 'getName', ''] 160 | ]; 161 | } 162 | 163 | /** 164 | * @return Codec|object 165 | */ 166 | protected function getInstanceWithoutConstructor() 167 | { 168 | return (new \ReflectionClass(Codec::class)) 169 | ->newInstanceWithoutConstructor(); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/Event/EventProgress.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Event; 14 | 15 | use Arhitector\Transcoder\Format\FormatInterface; 16 | use League\Event\Event; 17 | use League\Event\EventInterface; 18 | 19 | /** 20 | * Class EventProgress. 21 | * 22 | * @package Arhitector\Transcoder\Event 23 | */ 24 | class EventProgress extends Event implements EventInterface 25 | { 26 | 27 | /** 28 | * @var int Total pass value. 29 | */ 30 | protected $totalPass; 31 | 32 | /** 33 | * @var float Duration value. 34 | */ 35 | protected $duration = 0.0; 36 | 37 | /** 38 | * @var int Current pass. 39 | */ 40 | protected $currentPass = 1; 41 | 42 | /** 43 | * @var float Current time in seconds. 44 | */ 45 | protected $time = 0.0; 46 | 47 | /** 48 | * @var int Current file size. 49 | */ 50 | protected $size = 0; 51 | 52 | /** 53 | * @var FormatInterface 54 | */ 55 | protected $format; 56 | 57 | /** 58 | * EventProgress constructor. 59 | * 60 | * @param string $pass 61 | * @param FormatInterface $format 62 | */ 63 | public function __construct($pass, FormatInterface $format) 64 | { 65 | $this->setCurrentPass((int) $pass); 66 | $this->setDuration($format->getDuration()->toSeconds()); 67 | $this->setTotalPass($format->getPasses()); 68 | $this->format = $format; 69 | 70 | parent::__construct('progress'); 71 | } 72 | 73 | /** 74 | * Set the current pass value. 75 | * 76 | * @param int $currentPass 77 | * 78 | * @return EventProgress 79 | */ 80 | public function setCurrentPass($currentPass) 81 | { 82 | $this->currentPass = $currentPass; 83 | 84 | return $this; 85 | } 86 | 87 | /** 88 | * Set current time value. 89 | * 90 | * @param float $time 91 | * 92 | * @return EventProgress 93 | */ 94 | public function setCurrentTime($time) 95 | { 96 | $this->time = $time; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Set current size value. 103 | * 104 | * @param int $size 105 | * 106 | * @return EventProgress 107 | */ 108 | public function setCurrentSize($size) 109 | { 110 | $this->size = $size; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Get current percent or '-1'. 117 | * 118 | * @return float 119 | */ 120 | public function getPercent() 121 | { 122 | if ($this->duration == 0) 123 | { 124 | return -1; 125 | } 126 | 127 | $percent = $this->time / $this->duration * 100 / $this->totalPass; 128 | 129 | return round(min(100, $percent + 100 / $this->totalPass * ($this->currentPass - 1)), 2); 130 | } 131 | 132 | /** 133 | * Get remaining time. 134 | * 135 | * @return float 136 | */ 137 | public function getRemaining() 138 | { 139 | return $this->duration - $this->time; 140 | } 141 | 142 | /** 143 | * Emit an event. 144 | * 145 | * @param string $type 146 | * @param string $data 147 | * 148 | * @return mixed 149 | */ 150 | public function __invoke($type, $data) 151 | { 152 | if (preg_match('/size=(.*?) time=(.*?) /', $data, $matches)) 153 | { 154 | $matches[2] = array_reverse(explode(':', $matches[2])); 155 | $duration = (float) array_shift($matches[2]); 156 | 157 | foreach ($matches[2] as $key => $value) 158 | { 159 | $duration += (int) $value * (60 ** ($key + 1)); 160 | } 161 | 162 | $this->setCurrentSize((int) trim($matches[1])); 163 | $this->setCurrentTime($duration); 164 | 165 | $this->format->emit($this); 166 | } 167 | } 168 | 169 | /** 170 | * Set total passes. 171 | * 172 | * @param int $totalPass 173 | * 174 | * @return EventProgress 175 | * @throws \InvalidArgumentException 176 | */ 177 | protected function setTotalPass($totalPass) 178 | { 179 | if ($totalPass < 1) 180 | { 181 | throw new \InvalidArgumentException('The total passes value cannot be less than 1.'); 182 | } 183 | 184 | $this->totalPass = (int) $totalPass; 185 | 186 | return $this; 187 | } 188 | 189 | /** 190 | * Sets duration value. 191 | * 192 | * @param float $duration 193 | * 194 | * @return EventProgress 195 | * @throws \InvalidArgumentException 196 | */ 197 | protected function setDuration($duration) 198 | { 199 | if ( ! is_numeric($duration)) 200 | { 201 | throw new \InvalidArgumentException('The duration value must be a float type.'); 202 | } 203 | 204 | $this->duration = abs($duration); 205 | 206 | return $this; 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /src/Format/AudioFormat.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class AudioFormat. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class AudioFormat implements AudioFormatInterface 23 | { 24 | use FormatTrait; 25 | 26 | /** 27 | * @var int Audio bitrate value. 28 | */ 29 | protected $audioBitrate = 0; 30 | 31 | /** 32 | * @var int Audio channels value. 33 | */ 34 | protected $audioChannels; 35 | 36 | /** 37 | * @var Codec Audio codec value. 38 | */ 39 | protected $audioCodec; 40 | 41 | /** 42 | * @var int Audio sample frequency value. 43 | */ 44 | protected $audioFrequency; 45 | 46 | /** 47 | * @var string[] The list of available audio codecs. 48 | */ 49 | protected $audioAvailableCodecs = []; 50 | 51 | /** 52 | * Format constructor. 53 | * 54 | * @param Codec|string $audioCodec 55 | * 56 | * @throws \InvalidArgumentException 57 | */ 58 | public function __construct($audioCodec = null) 59 | { 60 | if ($audioCodec !== null) 61 | { 62 | if ( ! $audioCodec instanceof Codec) 63 | { 64 | $audioCodec = new Codec($audioCodec, ''); 65 | } 66 | 67 | $this->setAudioCodec($audioCodec); 68 | } 69 | 70 | $this->setAudioBitrate(128000); 71 | } 72 | 73 | /** 74 | * Gets the audio channels value. 75 | * 76 | * @return int 77 | */ 78 | public function getChannels() 79 | { 80 | return $this->audioChannels; 81 | } 82 | 83 | /** 84 | * Sets the channels value. 85 | * 86 | * @param int $channels 87 | * 88 | * @return AudioFormatInterface 89 | * @throws \InvalidArgumentException 90 | */ 91 | public function setChannels($channels) 92 | { 93 | if ( ! is_numeric($channels) || $channels < 1) 94 | { 95 | throw new \InvalidArgumentException('Wrong audio channels value.'); 96 | } 97 | 98 | $this->audioChannels = $channels; 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * Get audio codec. 105 | * 106 | * @return Codec 107 | */ 108 | public function getAudioCodec() 109 | { 110 | return $this->audioCodec; 111 | } 112 | 113 | /** 114 | * Sets the audio codec, Should be in the available ones, otherwise an exception is thrown. 115 | * 116 | * @param Codec $codec 117 | * 118 | * @return AudioFormatInterface 119 | * @throws \InvalidArgumentException 120 | */ 121 | public function setAudioCodec(Codec $codec) 122 | { 123 | if ($this->getAvailableAudioCodecs() && ! in_array((string) $codec, $this->getAvailableAudioCodecs(), false)) 124 | { 125 | throw new \InvalidArgumentException(sprintf('Wrong audio codec value for "%s", available values are %s', 126 | $codec, implode(', ', $this->getAvailableAudioCodecs()))); 127 | } 128 | 129 | $this->audioCodec = $codec; 130 | 131 | return $this; 132 | } 133 | 134 | /** 135 | * Get the audio bitrate value. 136 | * 137 | * @return int 138 | */ 139 | public function getAudioBitrate() 140 | { 141 | return $this->audioBitrate; 142 | } 143 | 144 | /** 145 | * Sets the audio bitrate value. 146 | * 147 | * @param int $bitrate 148 | * 149 | * @return AudioFormatInterface 150 | * @throws \InvalidArgumentException 151 | */ 152 | public function setAudioBitrate($bitrate) 153 | { 154 | if ( ! is_numeric($bitrate) || $bitrate < 0) 155 | { 156 | throw new \InvalidArgumentException('The audio bitrate value must be a integer type.'); 157 | } 158 | 159 | $this->audioBitrate = (int) $bitrate; 160 | 161 | return $this; 162 | } 163 | 164 | /** 165 | * Get frequency value. 166 | * 167 | * @return int 168 | */ 169 | public function getFrequency() 170 | { 171 | return $this->audioFrequency; 172 | } 173 | 174 | /** 175 | * Set frequency value. 176 | * 177 | * @param int $frequency 178 | * 179 | * @return AudioFormatInterface 180 | * @throws \InvalidArgumentException 181 | */ 182 | public function setFrequency($frequency) 183 | { 184 | if ( ! is_numeric($frequency) || $frequency < 1) 185 | { 186 | throw new \InvalidArgumentException('Wrong sample frequency value.'); 187 | } 188 | 189 | $this->audioFrequency = $frequency; 190 | 191 | return $this; 192 | } 193 | 194 | /** 195 | * Get available codecs. 196 | * 197 | * @return string[] 198 | */ 199 | public function getAvailableAudioCodecs() 200 | { 201 | return $this->audioAvailableCodecs; 202 | } 203 | 204 | /** 205 | * Returns the number of passes. 206 | * 207 | * @return int 208 | */ 209 | public function getPasses() 210 | { 211 | return 1; 212 | } 213 | 214 | /** 215 | * Sets the list of available audio codecs. 216 | * 217 | * @param array $codecs 218 | * @param bool $force 219 | * 220 | * @return \Arhitector\Transcoder\Format\AudioFormat 221 | */ 222 | protected function setAvailableAudioCodecs(array $codecs, $force = false) 223 | { 224 | if ( ! $force && $this->getAvailableAudioCodecs()) 225 | { 226 | $codecs = array_intersect($this->getAvailableAudioCodecs(), $codecs); 227 | } 228 | 229 | $this->audioAvailableCodecs = array_map('strval', $codecs); 230 | 231 | return $this; 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /src/Frame.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | use Arhitector\Transcoder\Exception\TranscoderException; 17 | use Arhitector\Transcoder\Filter\FilterInterface; 18 | use Arhitector\Transcoder\Filter\FrameFilterInterface; 19 | use Arhitector\Transcoder\Filter\SimpleFilter; 20 | use Arhitector\Transcoder\Format\FormatInterface; 21 | use Arhitector\Transcoder\Format\FrameFormat; 22 | use Arhitector\Transcoder\Format\FrameFormatInterface; 23 | use Arhitector\Transcoder\Format\VideoFormat; 24 | use Arhitector\Transcoder\Format\VideoFormatInterface; 25 | use Arhitector\Transcoder\Stream\Collection; 26 | use Arhitector\Transcoder\Stream\FrameStream; 27 | use Arhitector\Transcoder\Stream\VideoStream; 28 | 29 | /** 30 | * Class Frame. 31 | * 32 | * @package Arhitector\Transcoder 33 | */ 34 | class Frame implements FrameInterface 35 | { 36 | use TranscodeTrait; 37 | 38 | /** 39 | * Returns the video codec. 40 | * 41 | * @return Codec|null 42 | */ 43 | public function getVideoCodec() 44 | { 45 | return $this->getFormat()->getVideoCodec(); 46 | } 47 | 48 | /** 49 | * Get width value. 50 | * 51 | * @return int 52 | */ 53 | public function getWidth() 54 | { 55 | return $this->getFormat()->getWidth(); 56 | } 57 | 58 | /** 59 | * Get height value. 60 | * 61 | * @return int 62 | */ 63 | public function getHeight() 64 | { 65 | return $this->getFormat()->getHeight(); 66 | } 67 | 68 | /** 69 | * Get current format. 70 | * 71 | * @return FrameFormatInterface 72 | */ 73 | public function getFormat() 74 | { 75 | return $this->format; 76 | } 77 | 78 | /** 79 | * Get duration value. 80 | * 81 | * @return float 82 | */ 83 | public function getDuration() 84 | { 85 | return 0.0; 86 | } 87 | 88 | /** 89 | * Add a new filter. 90 | * 91 | * @param FilterInterface $filter 92 | * @param int $priority range 0-99. 93 | * 94 | * @return TranscodeInterface 95 | * @throws \InvalidArgumentException 96 | * @throws InvalidFilterException 97 | */ 98 | public function addFilter(FilterInterface $filter, $priority = 0) 99 | { 100 | if ( ! $filter instanceof FrameFilterInterface) 101 | { 102 | throw new InvalidFilterException('Filter type is not supported.'); 103 | } 104 | 105 | $this->filters->insert($filter, $priority); 106 | 107 | return $this; 108 | } 109 | 110 | /** 111 | * Initializing. 112 | * 113 | * @param \StdClass $demuxing 114 | * 115 | * @return void 116 | */ 117 | protected function initialize(\StdClass $demuxing) 118 | { 119 | $defaultFormat = FrameFormat::class; 120 | 121 | foreach ($demuxing->streams as $stream) 122 | { 123 | if (isset($stream['type']) && strtolower($stream['type']) == 'audio') 124 | { 125 | $defaultFormat = VideoFormat::class; 126 | } 127 | } 128 | 129 | /** @var FrameFormatInterface $format */ 130 | $format = $this->findFormatClass($demuxing->format['format'], $defaultFormat); 131 | 132 | if ( ! is_subclass_of($format, FrameFormatInterface::class)) 133 | { 134 | throw new TranscoderException(sprintf('This format unsupported in the "%s" wrapper.', __CLASS__)); 135 | } 136 | 137 | $streams = new Collection(); 138 | 139 | foreach ($demuxing->streams as $number => $stream) 140 | { 141 | if (isset($stream['type']) && strtolower($stream['type']) == 'video') 142 | { 143 | $streams[$number] = is_subclass_of($format, VideoFormatInterface::class) 144 | ? VideoStream::create($this, $stream) : FrameStream::create($this, $stream); 145 | } 146 | } 147 | 148 | $this->setStreams($streams); 149 | 150 | if ($stream = $this->getStreams(self::STREAM_FRAME | self::STREAM_VIDEO)->getFirst()) 151 | { 152 | foreach ($stream->toArray() as $key => $value) 153 | { 154 | if ($key != 'metadata') 155 | { 156 | $demuxing->format[$key] = $value; 157 | } 158 | 159 | if ($key == 'codec') 160 | { 161 | $demuxing->format['video_codec'] = $value; 162 | } 163 | } 164 | } 165 | 166 | if (isset($demuxing->format['codecs']) && is_array($demuxing->format['codecs'])) 167 | { 168 | $demuxing->format['available_video_codecs'] = array_keys(array_filter($demuxing->format['codecs'], function ($mask) { 169 | return $mask & 2; 170 | })); 171 | } 172 | 173 | $this->setFormat($format::fromArray(array_filter($demuxing->format, function ($value) { 174 | return $value !== null; 175 | }))); 176 | 177 | if ($this->getFormat() instanceof VideoFormatInterface) 178 | { 179 | $this->addFilter(new SimpleFilter([ 180 | 'frames:v' => 1, 181 | 'seek_start' => 0, 182 | ]), 0); 183 | } 184 | } 185 | 186 | /** 187 | * It supports the type of media. 188 | * 189 | * @return bool 190 | */ 191 | protected function isSupportedFileType() 192 | { 193 | return ! (stripos($this->getMimeType(), 'image/') !== 0); 194 | } 195 | 196 | /** 197 | * Checks is supported the encoding in format. 198 | * 199 | * @param FormatInterface $format 200 | * 201 | * @return bool 202 | */ 203 | protected function isSupportedFormat(FormatInterface $format) 204 | { 205 | return $format instanceof FrameFormatInterface; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/Service/Heap.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Service; 14 | 15 | /** 16 | * Class Heap 17 | * 18 | * @package Arhitector\Transcoder\Service 19 | */ 20 | class Heap extends \SplHeap 21 | { 22 | 23 | /** 24 | * @var array 25 | */ 26 | protected $heap = []; 27 | 28 | /** 29 | * Heap constructor. 30 | * 31 | * @param array $options 32 | */ 33 | public function __construct(array $options = []) 34 | { 35 | foreach ($options as $option => $value) 36 | { 37 | $this->push($option, $value); 38 | } 39 | } 40 | 41 | /** 42 | * Pushes an element at the end of the doubly linked list. 43 | * 44 | * @param string $option 45 | * @param mixed $value 46 | */ 47 | public function push($option, $value) 48 | { 49 | if ( ! is_string($option)) 50 | { 51 | throw new \InvalidArgumentException('The option value must be a string type.'); 52 | } 53 | 54 | $this->heap[$option][] = $value; 55 | } 56 | 57 | /** 58 | * Checks whether the key exists. 59 | * 60 | * @param string $option 61 | * 62 | * @return bool 63 | */ 64 | public function has($option) 65 | { 66 | return array_key_exists($option, $this->heap); 67 | } 68 | 69 | /** 70 | * Returns a value by key. 71 | * 72 | * @param $option 73 | * 74 | * @return array|mixed 75 | */ 76 | public function get($option) 77 | { 78 | if ($this->has($option)) 79 | { 80 | return $this->heap[$option]; 81 | } 82 | 83 | return []; 84 | } 85 | 86 | /** 87 | * Rewind iterator back to the start (no-op) 88 | * 89 | * @return void 90 | */ 91 | public function rewind() 92 | { 93 | foreach ($this->heap as $option => $value) 94 | { 95 | $this->insert([$option, $value]); 96 | } 97 | 98 | parent::rewind(); 99 | } 100 | 101 | /** 102 | * Return current node pointed by the iterator 103 | * 104 | * @return mixed 105 | */ 106 | public function current() 107 | { 108 | list($option, $values) = parent::current(); 109 | 110 | if (isset($this->getAliasOptions()[ltrim($option, '-')])) 111 | { 112 | $option = $this->getAliasOptions()[ltrim($option, '-')]; 113 | } 114 | 115 | $option = $option[0] == '-' ? $option : '-'.$option; 116 | 117 | if ($option == '-output') 118 | { 119 | return []; 120 | } 121 | 122 | $options = []; 123 | 124 | if ($option == '-map' || $option == '-metadata') 125 | { 126 | foreach ($option == '-metadata' ? array_merge(...$values) : $values as $key => $value) 127 | { 128 | $options[] = $option; 129 | $options[] = is_int($key) ? $value : sprintf('%s=%s', $key, $value); 130 | } 131 | } 132 | else if (stripos($option, 'filter') === 1) 133 | { 134 | // array_merge_recursive(...$values) 135 | $options[] = $option; 136 | $options[] = implode('; ', $values); 137 | } 138 | else if (($value = array_pop($values)) !== false) 139 | { 140 | $options[] = $option; 141 | 142 | if (is_scalar($value) && trim($value) != '' && $value !== true) 143 | { 144 | $options[] = $value; 145 | } 146 | } 147 | 148 | return $options; 149 | } 150 | 151 | private function isStackable($option) 152 | { 153 | return in_array($option, ['-map', /*'-input'*/]); 154 | } 155 | 156 | /** 157 | * The alias of options. 158 | * 159 | * @return array 160 | */ 161 | public function getAliasOptions() 162 | { 163 | return [ 164 | 'input' => '-i', 165 | 'disable_audio' => '-an', 166 | 'disable_video' => '-vn', 167 | 'disable_subtitle' => '-sn', 168 | 'audio_quality' => '-qscale:a', 169 | 'audio_codec' => '-codec:a', 170 | 'audio_bitrate' => '-b:a', 171 | 'audio_sample_frequency' => '-ar', 172 | 'audio_channels' => '-ac', 173 | 'video_quality' => '-qscale:v', 174 | 'video_codec' => '-codec:v', 175 | 'video_aspect_ratio' => '-aspect', 176 | 'video_frame_rate' => '-r', 177 | 'video_max_frames' => '-vframes', 178 | 'video_bitrate' => '-b:v', 179 | 'video_pixel_format' => '-pix_fmt', 180 | 'metadata' => '-metadata', 181 | 'force_format' => '-f', 182 | 'seek_start' => '-ss', 183 | 'seek_end' => '-t' 184 | ]; 185 | } 186 | 187 | /** 188 | * Compare elements in order to place them correctly in the heap while sifting up. 189 | * 190 | * @param mixed $value1 The value of the first node being compared. 191 | * @param mixed $value2 The value of the second node being compared. 192 | * 193 | * @return int Result of the comparison. 194 | */ 195 | protected function compare($value1, $value2) 196 | { 197 | $haystack = [ 198 | 'y', 199 | 'ignore_unknown', 200 | 'stream_loop', 201 | 'sseof', 202 | 'itsoffset', 203 | 'thread_queue_size', 204 | 'seek_timestamp', 205 | 'accurate_seek', 206 | 'noaccurate_seek', 207 | 'ss', 208 | 'seek_start', 209 | 'i', 210 | 'input', 211 | 212 | ]; 213 | 214 | if (($value1 = array_search(ltrim($value1[0], '-'), $haystack, false)) !== false) 215 | { 216 | return ($value2 = array_search(ltrim($value2[0], '-'), $haystack)) !== false && $value1 > $value2 ? -1 : 1; 217 | } 218 | 219 | return -1; 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/Audio.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | use Arhitector\Transcoder\Exception\TranscoderException; 17 | use Arhitector\Transcoder\Filter\AudioFilterInterface; 18 | use Arhitector\Transcoder\Filter\FilterInterface; 19 | use Arhitector\Transcoder\Format\AudioFormat; 20 | use Arhitector\Transcoder\Format\AudioFormatInterface; 21 | use Arhitector\Transcoder\Format\FormatInterface; 22 | use Arhitector\Transcoder\Format\FrameFormatInterface; 23 | use Arhitector\Transcoder\Stream\AudioStream; 24 | use Arhitector\Transcoder\Stream\AudioStreamInterface; 25 | use Arhitector\Transcoder\Stream\Collection; 26 | use Arhitector\Transcoder\Stream\FrameStream; 27 | use Arhitector\Transcoder\Stream\VideoStream; 28 | 29 | /** 30 | * Class Audio. 31 | * 32 | * @package Arhitector\Transcoder 33 | */ 34 | class Audio implements AudioInterface 35 | { 36 | use TranscodeTrait; 37 | 38 | /** 39 | * Gets the audio channels value. 40 | * 41 | * @return int 42 | */ 43 | public function getAudioChannels() 44 | { 45 | return $this->getFormat()->getChannels(); 46 | } 47 | 48 | /** 49 | * Gets the audio kilo bitrate value. 50 | * 51 | * @return int 52 | */ 53 | public function getAudioKiloBitrate() 54 | { 55 | return (int) ($this->getFormat()->getAudioBitrate() / 1000); 56 | } 57 | 58 | /** 59 | * Returns the audio codec. 60 | * 61 | * @return Codec|null 62 | */ 63 | public function getAudioCodec() 64 | { 65 | return $this->getFormat()->getAudioCodec(); 66 | } 67 | 68 | /** 69 | * Get sample frequency value. 70 | * 71 | * @return int 72 | */ 73 | public function getFrequency() 74 | { 75 | return $this->getFormat()->getFrequency(); 76 | } 77 | 78 | /** 79 | * Get duration value. 80 | * 81 | * @return float 82 | */ 83 | public function getDuration() 84 | { 85 | return $this->getFormat()->getDuration()->toSeconds(); 86 | } 87 | 88 | /** 89 | * Get current format. 90 | * 91 | * @return AudioFormatInterface 92 | */ 93 | public function getFormat() 94 | { 95 | return $this->format; 96 | } 97 | 98 | /** 99 | * Add a new filter. 100 | * 101 | * @param FilterInterface $filter 102 | * @param int $priority range 0-99. 103 | * 104 | * @return TranscodeInterface 105 | * @throws \RangeException 106 | * @throws InvalidFilterException 107 | */ 108 | public function addFilter(FilterInterface $filter, $priority = 0) 109 | { 110 | if ( ! $filter instanceof AudioFilterInterface) 111 | { 112 | throw new InvalidFilterException('Filter type is not supported.'); 113 | } 114 | 115 | if ($priority > 99) 116 | { 117 | throw new \RangeException('Priority should be in the range from 0 to 99.'); 118 | } 119 | 120 | $this->filters->insert($filter, $priority); 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * Initializing. 127 | * 128 | * @param \StdClass $demuxing 129 | * 130 | * @return void 131 | */ 132 | protected function initialize(\StdClass $demuxing) 133 | { 134 | $format = $this->findFormatClass($demuxing->format['format'], AudioFormat::class); 135 | 136 | if ( ! is_subclass_of($format, AudioFormatInterface::class)) 137 | { 138 | throw new TranscoderException(sprintf('This format unsupported in the "%s" wrapper.', __CLASS__)); 139 | } 140 | 141 | $streams = []; 142 | 143 | foreach ($demuxing->streams as $number => $stream) 144 | { 145 | $stream['type'] = isset($stream['type']) ? strtolower($stream['type']) : null; 146 | 147 | if ($stream['type'] == 'audio') 148 | { 149 | $streams[$number] = AudioStream::create($this, $stream); 150 | } 151 | else if ($stream['type'] == 'video') 152 | { 153 | if ($this instanceof AudioInterface && $this instanceof VideoInterface) 154 | { 155 | $streams[$number] = VideoStream::create($this, $stream); 156 | } 157 | else 158 | { 159 | $streams[$number] = FrameStream::create($this, $stream); 160 | } 161 | } 162 | } 163 | 164 | $this->setStreams(new Collection($streams)); 165 | 166 | foreach ($this->getStreams(self::STREAM_AUDIO | self::STREAM_FRAME) as $stream) 167 | { 168 | $prefix = $stream instanceof AudioStreamInterface ? 'audio_' : 'video_'; 169 | 170 | foreach ($stream->toArray() as $key => $value) 171 | { 172 | if ($key != 'metadata') 173 | { 174 | $demuxing->format[$key] = $value; 175 | } 176 | 177 | if (in_array($key, ['codec', 'bitrate'], false)) 178 | { 179 | $demuxing->format[$prefix.$key] = $value; 180 | } 181 | } 182 | } 183 | 184 | if (isset($demuxing->format['codecs']) && is_array($demuxing->format['codecs'])) 185 | { 186 | $demuxing->format['available_audio_codecs'] = array_keys(array_filter($demuxing->format['codecs'], function ($mask) { 187 | return $mask & 2; 188 | })); 189 | } 190 | 191 | $this->setFormat($format::fromArray(array_filter($demuxing->format, function ($value) { 192 | return $value !== null; 193 | }))); 194 | } 195 | 196 | /** 197 | * It supports the type of media. 198 | * 199 | * @return bool 200 | */ 201 | protected function isSupportedFileType() 202 | { 203 | return ! (stripos($this->getMimeType(), 'audio/') !== 0); 204 | } 205 | 206 | /** 207 | * Checks is supported the encoding in format. 208 | * 209 | * @param FormatInterface $format 210 | * 211 | * @return bool 212 | */ 213 | protected function isSupportedFormat(FormatInterface $format) 214 | { 215 | return $format instanceof AudioFormatInterface && ! $format instanceof FrameFormatInterface; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /bin/ffmpeg_fmt.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | 14 | /** 15 | * 'FFMpeg format 1' => [extensions], 16 | * 'FFMpeg format 2' => [extensions, ...], 17 | */ 18 | return [ 19 | 'a64' => ['a64', 'A64'], 20 | 'ace' => ['ac3', 'eac3'], 21 | 'adts' => ['aac', 'adts'], 22 | 'adx' => ['adx'], 23 | 'aea' => ['aea'], 24 | 'aiff' => ['aif', 'aiff', 'afc', 'aifc'], 25 | 'amr' => ['amr'], 26 | 'ape' => ['ape', 'apl', 'mac'], 27 | 'aqtitle' => ['aqt'], 28 | 'asf' => ['asf', 'wmv', 'wma'], 29 | 'asf_stream' => ['asf', 'wmv', 'wma'], 30 | 'ass' => ['ass', 'ssa'], 31 | 'ast' => ['ast'], 32 | 'au' => ['au'], 33 | 'avi' => ['avi'], 34 | 'avisynth' => ['avs'], 35 | 'avs' => ['avs'], 36 | 'avr' => ['avr'], 37 | 'bin' => ['bin'], 38 | 'adf' => ['adf'], 39 | 'idf' => ['idf'], 40 | 'bit' => ['bit'], 41 | 'bmv' => ['bmv'], 42 | 'brstm' => ['brstm'], 43 | 'caf' => ['caf'], 44 | 'cdg' => ['cdg'], 45 | 'cdxl' => ['cdxl', 'xl'], 46 | 'daud' => ['302', 'daud'], 47 | 'dts' => ['dts'], 48 | 'dtshd' => ['dtshd'], 49 | 'dv' => ['dv', 'dif'], 50 | 'ea_cdata' => ['cdata'], 51 | 'epaf' => ['pgaf', 'fap'], 52 | 'ffm' => ['ffm'], 53 | 'ffmetadata' => ['ffmeta'], 54 | 'filmstrip' => ['flm'], 55 | 'flac' => ['flac'], 56 | 'flv' => ['flv'], 57 | 'g722' => ['g722', '722'], 58 | 'g723_1' => ['tco', 'rco', 'g723_1'], 59 | 'g729' => ['g729'], 60 | 'gif' => ['gif'], 61 | 'gsm' => ['gsm'], 62 | 'gxf' => ['gxf'], 63 | 'hls' => ['m3u8'], 64 | 'ico' => ['ico'], 65 | 'roq' => ['roq'], 66 | 'ilbc' => ['lbc'], 67 | 'image2' => ['bmp', 'dpx', 'jls', 'jpeg', 'jpg', 'ljpg', 'pam', 'pbm', 'pcx', 'pgm', 'pgmyuv', 'png', 'ppm', 'sgi', 'tga', 'tif', 'tiff', 'jp2', 'j2c', 'xwd', 'sun', 'ras', 'rs', 'im1', 'im8', 'im24', 'sunras', 'xbm', 'xface'], 68 | 'ingenient' => ['cgi'], 69 | 'ircam' => ['sf', 'ircam'], 70 | 'ivf' => ['ivf'], 71 | 'jacosub' => ['jss', 'js'], 72 | 'latm' => ['latm', 'loas'], 73 | 'libmodplug' => ['669', 'abc', 'amf', 'ams', 'dbm', 'dmf', 'dsm', 'far', 'it', 'mdl', 'med', 'mid', 'mod', 'mt2', 'mtm', 'okt', 'psm', 'ptm', 's3m', 'stm', 'ult', 'umx', 'xm', 'itgz', 'itr', 'itz', 'mdgz', 'mdr', 'mdz', 's3gz', 's3r', 's3z', 'xmgz', 'xmr', 'xmz'], 74 | 'libnut' => ['nut'], 75 | 'lvf' => ['lvf'], 76 | 'matroska' => ['mkv', 'mka'], 77 | 'webm' => ['webm'], 78 | 'microdvd' => ['sub'], 79 | 'mmf' => ['mmf'], 80 | 'mov' => ['mov'], 81 | '3gp' => ['3gp'], 82 | 'mp4' => ['mp4'], 83 | 'psp' => ['mp4', 'psp'], 84 | '3g2' => ['3g2'], 85 | 'ipod' => ['m4v', 'm4a'], 86 | 'ismv' => ['ismv', 'isma'], 87 | 'f4v' => ['f4v'], 88 | 'mp3' => ['mp2', 'mp3', 'm2a'], 89 | 'mp2' => ['mp2', 'm2a', 'mp3'], 90 | 'mpc' => ['mpc'], 91 | 'vobsub' => ['idx'], 92 | 'mpeg' => ['mpg', 'mpeg'], 93 | 'vob' => ['vob'], 94 | 'svcd' => ['vob'], 95 | 'dvd' => ['dvd'], 96 | 'mpegts' => ['ts', 'm2t', 'm2ts', 'mts'], 97 | 'mpjpeg' => ['mjpg'], 98 | 'mpl2' => ['txt', 'mpl2'], 99 | 'mpsub' => ['sub'], 100 | 'mvi' => ['mvi'], 101 | 'mxf' => ['mxf'], 102 | 'mxg' => ['mxg'], 103 | 'nc' => ['v'], 104 | 'nistsphere' => ['nist', 'sph'], 105 | 'nut' => ['nut'], 106 | 'ogg' => ['ogg', 'ogv', 'spx', 'opus'], 107 | 'oga' => ['oga'], 108 | 'oma' => ['oma', 'omg', 'aa3'], 109 | 's16be' => ['sw'], 110 | 's16le' => ['sw'], 111 | 's8' => ['sb'], 112 | 'u16be' => ['uw'], 113 | 'u16le' => ['uw'], 114 | 'u8' => ['ub'], 115 | 'alaw' => ['al'], 116 | 'mulaw' => ['ul'], 117 | 'pjs' => ['pjs'], 118 | 'pvf' => ['pvf'], 119 | 'mlp' => ['mlp'], 120 | 'truehd' => ['thd'], 121 | 'shn' => ['shn'], 122 | 'vc1' => ['vc1'], 123 | 'ac3' => ['ac3'], 124 | 'cavsvideo' => ['cavs'], 125 | 'dirac' => ['drc'], 126 | 'dnxhd' => ['dnxhd'], 127 | 'eac3' => ['eac3'], 128 | 'h261' => ['h261'], 129 | 'h263' => ['h263'], 130 | 'h264' => ['h264'], 131 | 'm4v' => ['m4v'], 132 | 'mjpeg' => ['mjpg', 'mjpeg'], 133 | 'mpeg1video' => ['mpg', 'mpeg', 'm1v'], 134 | 'mpeg2video' => ['m2v'], 135 | 'rawvideo' => ['yuv', 'cif', 'qcif', 'rgb'], 136 | 'realtext' => ['rt'], 137 | 'rm' => ['rm', 'ra'], 138 | 'rso' => ['rso'], 139 | 'sami' => ['smi', 'sami'], 140 | 'sbg' => ['sbg'], 141 | 'siff' => ['vb', 'son'], 142 | 'smjpeg' => ['mjpg'], 143 | 'sox' => ['sox'], 144 | 'spdif' => ['spdif'], 145 | 'srt' => ['srt'], 146 | 'subviewer1' => ['sub'], 147 | 'subviewer' => ['sub'], 148 | 'swf' => ['swf'], 149 | 'tak' => ['tak'], 150 | 'tta' => ['tta'], 151 | 'tty' => ['ans', 'art', 'asc', 'diz', 'ice', 'nfo', 'txt', 'vt'], 152 | 'rcv' => ['rcv'], 153 | 'vivo' => ['viv'], 154 | 'voc' => ['voc'], 155 | 'vplayer' => ['txt'], 156 | 'vqf' => ['vqf', 'vql', 'vqe'], 157 | 'wav' => ['wav'], 158 | 'w64' => ['w64'], 159 | 'webvtt' => ['vtt'], 160 | 'wtv' => ['wtv'], 161 | 'wv' => ['wv'], 162 | 'yop' => ['yop'], 163 | 'yuv4mpegpipe' => ['y4m'] 164 | ]; 165 | -------------------------------------------------------------------------------- /src/VideoInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | /** 16 | * Interface VideoInterface. 17 | * 18 | * @package Arhitector\Transcoder 19 | */ 20 | interface VideoInterface extends FrameInterface, AudioInterface 21 | { 22 | 23 | /** 24 | * @var string 720x480 ntsc 25 | */ 26 | const SIZE_NTSC = '720x480'; 27 | 28 | /** 29 | * @var string 720x576 pal 30 | */ 31 | const SIZE_PAL = '720x576'; 32 | 33 | /** 34 | * @var string 352x240 qntsc 35 | */ 36 | const SIZE_QNTSC = '352x240'; 37 | 38 | /** 39 | * @var string 352x288 qpal 40 | */ 41 | const SIZE_QPAL = '352x288'; 42 | 43 | /** 44 | * @var string 640x480 sntsc 45 | */ 46 | const SIZE_SNTSC = '640x480'; 47 | 48 | /** 49 | * @var string 768x576 spal 50 | */ 51 | const SIZE_SPAL = '768x576'; 52 | 53 | /** 54 | * @var string 352x240 film 55 | */ 56 | const SIZE_FILM = '352x240'; 57 | 58 | /** 59 | * @var string 352x240 ntsc-film 60 | */ 61 | const SIZE_NTSC_FILM = '352x240'; 62 | 63 | /** 64 | * @var string 128x96 sqcif 65 | */ 66 | const SIZE_SQCIF = '128x96'; 67 | 68 | /** 69 | * @var string 176x144 qcif 70 | */ 71 | const SIZE_QCIF = '176x144'; 72 | 73 | /** 74 | * @var string 352x288 cif 75 | */ 76 | const SIZE_CIF = '352x288'; 77 | 78 | /** 79 | * @var string 704x576 cif 80 | */ 81 | const SIZE_4CIF = '704x576'; 82 | 83 | /** 84 | * @var string 1408x1152 16 85 | */ 86 | const SIZE_16CIF = '1408x1152'; 87 | 88 | /** 89 | * @var string 160x120 qqvga 90 | */ 91 | const SIZE_QQVGA = '160x120'; 92 | 93 | /** 94 | * @var string 320x240 qvga 95 | */ 96 | const SIZE_QVGA = '320x240'; 97 | 98 | /** 99 | * @var string 640x480 vga 100 | */ 101 | const SIZE_VGA = '640x480'; 102 | 103 | /** 104 | * @var string 800x600 svga 105 | */ 106 | const SIZE_SVGA = '800x600'; 107 | 108 | /** 109 | * @var string 1024x768 xga 110 | */ 111 | const SIZE_XGA = '1024x768'; 112 | 113 | /** 114 | * @var string 1600x1200 uxga 115 | */ 116 | const SIZE_UXGA = '1600x1200'; 117 | 118 | /** 119 | * @var string 2048x1536 qxga 120 | */ 121 | const SIZE_QXGA = '2048x1536'; 122 | 123 | /** 124 | * @var string 1280x1024 sxga 125 | */ 126 | const SIZE_SXGA = '1280x1024'; 127 | 128 | /** 129 | * @var string 2560x2048 qsxga 130 | */ 131 | const SIZE_QSXGA = '2560x2048'; 132 | 133 | /** 134 | * @var string 5120x4096 hsxga 135 | */ 136 | const SIZE_HSXGA = '5120x4096'; 137 | 138 | /** 139 | * @var string 852x480 wvga 140 | */ 141 | const SIZE_WVGA = '852x480'; 142 | 143 | /** 144 | * @var string 1366x768 wxga 145 | */ 146 | const SIZE_WXGA = '1366x768'; 147 | 148 | /** 149 | * @var string 1600x1024 wsxga 150 | */ 151 | const SIZE_WSXGA = '1600x1024'; 152 | 153 | /** 154 | * @var string 1920x1200 wuxga 155 | */ 156 | const SIZE_WUXGA = '1920x1200'; 157 | 158 | /** 159 | * @var string 2560x1600 woxga 160 | */ 161 | const SIZE_WOXGA = '2560x1600'; 162 | 163 | /** 164 | * @var string 3200x2048 wqsxga 165 | */ 166 | const SIZE_WQSXGA = '3200x2048'; 167 | 168 | /** 169 | * @var string 3840x2400 wquxga 170 | */ 171 | const SIZE_WQUXGA = '3840x2400'; 172 | 173 | /** 174 | * @var string 6400x4096 whsxga 175 | */ 176 | const SIZE_WHSXGA = '6400x4096'; 177 | 178 | /** 179 | * @var string 7680x4800 whuxga 180 | */ 181 | const SIZE_WHUXGA = '7680x4800'; 182 | 183 | /** 184 | * @var string 320x200 cga 185 | */ 186 | const SIZE_CGA = '320x200'; 187 | 188 | /** 189 | * @var string 640x350 ega 190 | */ 191 | const SIZE_EGA = '640x350'; 192 | 193 | /** 194 | * @var string 852x480 hd480 195 | */ 196 | const SIZE_HD480 = '852x480'; 197 | 198 | /** 199 | * @var string 1280x720 hd720 200 | */ 201 | const SIZE_HD720 = '1280x720'; 202 | 203 | /** 204 | * @var string 1920x1080 hd1080 205 | */ 206 | const SIZE_HD1080 = '1920x1080'; 207 | 208 | /** 209 | * @var string 2048x1080 2k 210 | */ 211 | const SIZE_2K = '2048x1080'; 212 | 213 | /** 214 | * @var string 1998x1080 2kflat 215 | */ 216 | const SIZE_2KFLAT = '1998x1080'; 217 | 218 | /** 219 | * @var string 2048x858 2kscope 220 | */ 221 | const SIZE_2KSCOPE = '2048x858'; 222 | 223 | /** 224 | * @var string 4096x2160 4k 225 | */ 226 | const SIZE_4K = '4096x2160'; 227 | 228 | /** 229 | * @var string 3996x2160 4kflat 230 | */ 231 | const SIZE_4KFLAT = '3996x2160'; 232 | 233 | /** 234 | * @var string 4096x1716 4kscope 235 | */ 236 | const SIZE_4KSCOPE = '4096x1716'; 237 | 238 | /** 239 | * @var string 640x360 nhd 240 | */ 241 | const SIZE_NHD = '640x360'; 242 | 243 | /** 244 | * @var string 240x160 hqvga 245 | */ 246 | const SIZE_HQVGA = '240x160'; 247 | 248 | /** 249 | * @var string 400x240 wqvga 250 | */ 251 | const SIZE_WQVGA = '400x240'; 252 | 253 | /** 254 | * @var string 432x240 fwqvga 255 | */ 256 | const SIZE_FWQVGA = '432x240'; 257 | 258 | /** 259 | * @var string 480x320 hvga 260 | */ 261 | const SIZE_HVGA = '480x320'; 262 | 263 | /** 264 | * @var string 960x540 qhd 265 | */ 266 | const SIZE_QHD = '960x540'; 267 | 268 | /** 269 | * @var string 2048x1080 2kdci 270 | */ 271 | const SIZE_2KDCI = '2048x1080'; 272 | 273 | /** 274 | * @var string 4096x2160 4kdci 275 | */ 276 | const SIZE_4KDCI = '4096x2160'; 277 | 278 | /** 279 | * @var string 3840x2160 uhd2160 280 | */ 281 | const SIZE_UHD2160 = '3840x2160'; 282 | 283 | /** 284 | * @var string 7680x4320 uhd4320 285 | */ 286 | const SIZE_UHD4320 = '7680x4320'; 287 | 288 | /** 289 | * Gets the bitrate value. 290 | * 291 | * @return int 292 | */ 293 | public function getKiloBitrate(); 294 | 295 | /** 296 | * Get frame rate value. 297 | * 298 | * @return float 299 | */ 300 | public function getFrameRate(); 301 | 302 | /** 303 | * Return a new Frame from by time interval. 304 | * 305 | * @param TimeInterval|int|float $interval 306 | * 307 | * @return Frame 308 | */ 309 | public function getFrame($interval); 310 | 311 | } 312 | -------------------------------------------------------------------------------- /src/Format/VideoFormat.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder\Format; 14 | 15 | use Arhitector\Transcoder\Codec; 16 | 17 | /** 18 | * Class VideoFormat. 19 | * 20 | * @package Arhitector\Transcoder\Format 21 | */ 22 | class VideoFormat extends AudioFormat implements FrameFormatInterface, VideoFormatInterface 23 | { 24 | 25 | /** 26 | * @var Codec The video codec value. 27 | */ 28 | protected $videoCodec; 29 | 30 | /** 31 | * @var int The width value. 32 | */ 33 | protected $width; 34 | 35 | /** 36 | * @var int The height value. 37 | */ 38 | protected $height; 39 | 40 | /** 41 | * @var string[] The list of available video codecs. 42 | */ 43 | protected $videoAvailableCodecs = []; 44 | 45 | /** 46 | * @var int Passes value. 47 | */ 48 | protected $passes = 1; 49 | 50 | /** 51 | * @var float Frame rate value. 52 | */ 53 | protected $videoFrameRate; 54 | 55 | /** 56 | * @var int Video bit rate value. 57 | */ 58 | protected $videoBitrate; 59 | 60 | /** 61 | * Format constructor. 62 | * 63 | * @param Codec|string $audioCodec 64 | * @param Codec|string $videoCodec 65 | * 66 | * @throws \InvalidArgumentException 67 | */ 68 | public function __construct($audioCodec = null, $videoCodec = null) 69 | { 70 | parent::__construct($audioCodec); 71 | 72 | if ($videoCodec !== null) 73 | { 74 | if ( ! $videoCodec instanceof Codec) 75 | { 76 | $videoCodec = new Codec($videoCodec); 77 | } 78 | 79 | $this->setVideoCodec($videoCodec); 80 | } 81 | 82 | $this->setVideoBitrate(1000000); 83 | } 84 | 85 | /** 86 | * Get the video bitrate value. 87 | * 88 | * @return int 89 | */ 90 | public function getVideoBitrate() 91 | { 92 | return $this->videoBitrate; 93 | } 94 | 95 | /** 96 | * Set the bitrate value. 97 | * 98 | * @param int $bitrate 99 | * 100 | * @return VideoFormat 101 | * @throws \InvalidArgumentException 102 | */ 103 | public function setVideoBitrate($bitrate) 104 | { 105 | if ( ! is_numeric($bitrate) || $bitrate < 0) 106 | { 107 | throw new \InvalidArgumentException('The video bit rate value must be a integer type.'); 108 | } 109 | 110 | $this->videoBitrate = (int) $bitrate; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * Sets the number of passes. 117 | * 118 | * @param int $passes 119 | * 120 | * @return VideoFormat 121 | * @throws \InvalidArgumentException 122 | */ 123 | public function setPasses($passes) 124 | { 125 | if ( ! is_int($passes) || $passes < 1) 126 | { 127 | throw new \InvalidArgumentException('The passes value must be a number greater then zero.'); 128 | } 129 | 130 | $this->passes = $passes; 131 | 132 | return $this; 133 | } 134 | 135 | /** 136 | * Get the frame rate value. 137 | * 138 | * @return float 139 | */ 140 | public function getFrameRate() 141 | { 142 | return (float) $this->videoFrameRate; 143 | } 144 | 145 | /** 146 | * Set the frame rate value. 147 | * 148 | * @param float $frameRate 149 | * 150 | * @return VideoFormat 151 | * @throws \InvalidArgumentException 152 | */ 153 | public function setFrameRate($frameRate) 154 | { 155 | if ( ! is_numeric($frameRate) || $frameRate < 0) 156 | { 157 | throw new \InvalidArgumentException('Wrong the frame rate value.'); 158 | } 159 | 160 | $this->videoFrameRate = $frameRate; 161 | 162 | return $this; 163 | } 164 | 165 | /** 166 | * Returns the number of passes. 167 | * 168 | * @return int 169 | */ 170 | public function getPasses() 171 | { 172 | return $this->passes; 173 | } 174 | 175 | /** 176 | * Get width value. 177 | * 178 | * @return int 179 | */ 180 | public function getWidth() 181 | { 182 | return $this->width; 183 | } 184 | 185 | /** 186 | * Get height value. 187 | * 188 | * @return int 189 | */ 190 | public function getHeight() 191 | { 192 | return $this->height; 193 | } 194 | 195 | /** 196 | * Get the video/frame codec. 197 | * 198 | * @return Codec 199 | */ 200 | public function getVideoCodec() 201 | { 202 | return $this->videoCodec; 203 | } 204 | 205 | /** 206 | * Sets the video/frame codec, should be in the available ones, otherwise an exception is thrown. 207 | * 208 | * @param Codec $codec 209 | * 210 | * @return $this 211 | * @throws \InvalidArgumentException 212 | */ 213 | public function setVideoCodec(Codec $codec) 214 | { 215 | if ($this->getAvailableVideoCodecs() && ! in_array($codec, $this->getAvailableVideoCodecs(), false)) 216 | { 217 | throw new \InvalidArgumentException(sprintf('Wrong video codec value for "%s", available values are %s', 218 | $codec, implode(', ', $this->getAvailableVideoCodecs()))); 219 | } 220 | 221 | $this->videoCodec = $codec; 222 | 223 | return $this; 224 | } 225 | 226 | /** 227 | * Get available codecs. 228 | * 229 | * @return string[] 230 | */ 231 | public function getAvailableVideoCodecs() 232 | { 233 | return $this->videoAvailableCodecs; 234 | } 235 | 236 | /** 237 | * Set the width value. 238 | * 239 | * @param int $width 240 | * 241 | * @return $this 242 | * @throws \InvalidArgumentException 243 | */ 244 | protected function setWidth($width) 245 | { 246 | if ( ! is_numeric($width) || $width < 1) 247 | { 248 | throw new \InvalidArgumentException('Wrong the width value.'); 249 | } 250 | 251 | $this->width = $width; 252 | 253 | return $this; 254 | } 255 | 256 | /** 257 | * Set the height value. 258 | * 259 | * @param int $height 260 | * 261 | * @return self 262 | * @throws \InvalidArgumentException 263 | */ 264 | protected function setHeight($height) 265 | { 266 | if ( ! is_numeric($height) || $height < 1) 267 | { 268 | throw new \InvalidArgumentException('Wrong the height value.'); 269 | } 270 | 271 | $this->height = $height; 272 | 273 | return $this; 274 | } 275 | 276 | /** 277 | * Sets the list of available audio codecs. 278 | * 279 | * @param array $codecs 280 | * @param bool $force 281 | * 282 | * @return self 283 | */ 284 | protected function setAvailableVideoCodecs(array $codecs, $force = false) 285 | { 286 | if ( ! $force && $this->getAvailableVideoCodecs()) 287 | { 288 | $codecs = array_intersect($this->getAvailableVideoCodecs(), $codecs); 289 | } 290 | 291 | $this->videoAvailableCodecs = array_map('strval', $codecs); 292 | 293 | return $this; 294 | } 295 | 296 | } 297 | -------------------------------------------------------------------------------- /src/Video.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @license http://opensource.org/licenses/MIT MIT 11 | * @copyright Copyright (c) 2017 Dmitry Arhitector 12 | */ 13 | namespace Arhitector\Transcoder; 14 | 15 | use Arhitector\Transcoder\Exception\InvalidFilterException; 16 | use Arhitector\Transcoder\Exception\TranscoderException; 17 | use Arhitector\Transcoder\Filter\AudioFilterInterface; 18 | use Arhitector\Transcoder\Filter\FilterInterface; 19 | use Arhitector\Transcoder\Filter\FrameFilterInterface; 20 | use Arhitector\Transcoder\Filter\SimpleFilter; 21 | use Arhitector\Transcoder\Format\FormatInterface; 22 | use Arhitector\Transcoder\Format\VideoFormat; 23 | use Arhitector\Transcoder\Format\VideoFormatInterface; 24 | use Arhitector\Transcoder\Stream\AudioStream; 25 | use Arhitector\Transcoder\Stream\Collection; 26 | use Arhitector\Transcoder\Stream\FrameStream; 27 | use Arhitector\Transcoder\Stream\SubtitleStream; 28 | use Arhitector\Transcoder\Stream\VideoStream; 29 | use Arhitector\Transcoder\Stream\VideoStreamInterface; 30 | 31 | /** 32 | * Class Video. 33 | * 34 | * @package Arhitector\Transcoder 35 | */ 36 | class Video extends Audio implements VideoInterface 37 | { 38 | 39 | /** 40 | * Get current format. 41 | * 42 | * @return VideoFormatInterface|\Arhitector\Transcoder\Format\FormatInterface 43 | */ 44 | public function getFormat() 45 | { 46 | return parent::getFormat(); 47 | } 48 | 49 | /** 50 | * Returns the video codec. 51 | * 52 | * @return Codec|null 53 | */ 54 | public function getVideoCodec() 55 | { 56 | return $this->getFormat()->getVideoCodec(); 57 | } 58 | 59 | /** 60 | * Get width value. 61 | * 62 | * @return int 63 | */ 64 | public function getWidth() 65 | { 66 | return $this->getFormat()->getWidth(); 67 | } 68 | 69 | /** 70 | * Get height value. 71 | * 72 | * @return int 73 | */ 74 | public function getHeight() 75 | { 76 | return $this->getFormat()->getHeight(); 77 | } 78 | 79 | /** 80 | * Gets the bitrate value. 81 | * 82 | * @return int 83 | */ 84 | public function getKiloBitrate() 85 | { 86 | return (int) ($this->getFormat()->getVideoBitrate() / 1000); 87 | } 88 | 89 | /** 90 | * Get frame rate value. 91 | * 92 | * @return float 93 | */ 94 | public function getFrameRate() 95 | { 96 | return $this->getFormat()->getFrameRate(); 97 | } 98 | 99 | /** 100 | * Add a new filter. 101 | * 102 | * @param FilterInterface $filter 103 | * @param int $priority range 0-99. 104 | * 105 | * @return TranscodeInterface 106 | * @throws \InvalidArgumentException 107 | * @throws \RangeException 108 | * @throws InvalidFilterException 109 | */ 110 | public function addFilter(FilterInterface $filter, $priority = 0) 111 | { 112 | if ( ! $filter instanceof FrameFilterInterface && ! $filter instanceof AudioFilterInterface) 113 | { 114 | throw new InvalidFilterException('Filter type is not supported.'); 115 | } 116 | 117 | if ($priority > 99) 118 | { 119 | throw new \RangeException('Priority should be in the range from 0 to 99.'); 120 | } 121 | 122 | $this->filters->insert($filter, $priority); 123 | 124 | return $this; 125 | } 126 | 127 | /** 128 | * Return a new Frame from by time interval. 129 | * 130 | * @param TimeInterval|int|float $interval 131 | * 132 | * @return Frame 133 | */ 134 | public function getFrame($interval) 135 | { 136 | if ( ! $interval instanceof TimeInterval) 137 | { 138 | $interval = new TimeInterval($interval); 139 | } 140 | 141 | if ($interval->getSeconds() > $this->getDuration()) 142 | { 143 | throw new \OutOfRangeException('The interval may not be a more than '.$this->getDuration()); 144 | } 145 | 146 | $frame = new Frame($this->getFilePath(), $this->getService()); 147 | $frame->addFilter(new SimpleFilter(['seek_start' => $interval->__toString()]), 99); 148 | 149 | return $frame; 150 | } 151 | 152 | /** 153 | * It supports the type of media. 154 | * 155 | * @return bool 156 | */ 157 | protected function isSupportedFileType() 158 | { 159 | return ! (stripos($this->getMimeType(), 'video/') !== 0); 160 | } 161 | 162 | /** 163 | * Initializing. 164 | * 165 | * @param \StdClass $demuxing 166 | * 167 | * @return void 168 | */ 169 | protected function initialize(\StdClass $demuxing) 170 | { 171 | $format = $this->findFormatClass($demuxing->format['format'], VideoFormat::class); 172 | 173 | if ( ! is_subclass_of($format, VideoFormatInterface::class)) 174 | { 175 | throw new TranscoderException(sprintf('This format unsupported in the "%s" wrapper.', __CLASS__)); 176 | } 177 | 178 | $streams = []; 179 | 180 | foreach ($demuxing->streams as $number => $stream) 181 | { 182 | $stream['type'] = isset($stream['type']) ? strtolower($stream['type']) : null; 183 | 184 | if ($stream['type'] == 'audio') 185 | { 186 | $streams[] = AudioStream::create($this, $stream); 187 | } 188 | else if ($stream['type'] == 'video') 189 | { 190 | if ($this instanceof AudioInterface && $this instanceof VideoInterface) 191 | { 192 | $streams[] = VideoStream::create($this, $stream); 193 | } 194 | else 195 | { 196 | $streams[] = FrameStream::create($this, $stream); 197 | } 198 | } 199 | else if ($stream['type'] == 'subtitle') 200 | { 201 | $streams[] = SubtitleStream::create($this, $stream); 202 | } 203 | } 204 | 205 | $this->setStreams(new Collection($streams)); 206 | 207 | foreach ($this->getStreams(self::STREAM_AUDIO | self::STREAM_VIDEO) as $stream) 208 | { 209 | $prefix = $stream instanceof VideoStreamInterface ? 'video_' : 'audio_'; 210 | 211 | foreach ($stream->toArray() as $key => $value) 212 | { 213 | if ($key != 'metadata') 214 | { 215 | $demuxing->format[$key] = $value; 216 | } 217 | 218 | if (in_array($key, ['codec', 'bitrate'], false)) 219 | { 220 | $demuxing->format[$prefix.$key] = $value; 221 | } 222 | } 223 | } 224 | 225 | if (isset($demuxing->format['codecs']) && is_array($demuxing->format['codecs'])) 226 | { 227 | $demuxing->format['available_video_codecs'] = array_keys(array_filter($demuxing->format['codecs'], function ($mask) { 228 | return $mask & 2; 229 | })); 230 | } 231 | 232 | $this->setFormat($format::fromArray(array_filter($demuxing->format, function ($value) { 233 | return $value !== null; 234 | }))); 235 | } 236 | 237 | /** 238 | * Checks is supported the encoding in format. 239 | * 240 | * @param FormatInterface $format 241 | * 242 | * @return bool 243 | */ 244 | protected function isSupportedFormat(FormatInterface $format) 245 | { 246 | return $format instanceof VideoFormatInterface; 247 | } 248 | 249 | } 250 | --------------------------------------------------------------------------------