Merge branch 'master' into patch-1
This commit is contained in:
		
				commit
				
					
						d48cda2861
					
				
			
		
					 39 changed files with 1014 additions and 306 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -5,3 +5,4 @@ composer.phar | ||||||
| composer.lock | composer.lock | ||||||
| phpunit.xml | phpunit.xml | ||||||
| sami.phar | sami.phar | ||||||
|  | .idea/ | ||||||
							
								
								
									
										13
									
								
								.travis.yml
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								.travis.yml
									
										
									
									
									
								
							|  | @ -5,6 +5,12 @@ dist: trusty | ||||||
| branches: | branches: | ||||||
|   only: |   only: | ||||||
|     - master |     - master | ||||||
|  |     - v1.x | ||||||
|  | 
 | ||||||
|  | cache: | ||||||
|  |   directories: | ||||||
|  |     - $HOME/.composer/cache | ||||||
|  |     - $HOME/.cache | ||||||
| 
 | 
 | ||||||
| php: | php: | ||||||
|   - 5.4 |   - 5.4 | ||||||
|  | @ -12,11 +18,10 @@ php: | ||||||
|   - 5.6 |   - 5.6 | ||||||
|   - 7.0 |   - 7.0 | ||||||
|   - 7.1 |   - 7.1 | ||||||
|   - hhvm |   - 7.2 | ||||||
|  |   - 7.3 | ||||||
| 
 | 
 | ||||||
| matrix: | matrix: | ||||||
|   allow_failures: |  | ||||||
|     - php: hhvm |  | ||||||
|   include: |   include: | ||||||
|     - php: 5.4 |     - php: 5.4 | ||||||
|       env: COMPOSER_FLAGS="--prefer-lowest" |       env: COMPOSER_FLAGS="--prefer-lowest" | ||||||
|  | @ -29,7 +34,7 @@ before_install: | ||||||
| 
 | 
 | ||||||
| install: | install: | ||||||
|   - sudo apt-get install -y ffmpeg |   - sudo apt-get install -y ffmpeg | ||||||
|   - composer update --prefer-source $COMPOSER_FLAGS |   - composer update --prefer-dist $COMPOSER_FLAGS | ||||||
| 
 | 
 | ||||||
| script: | script: | ||||||
|   - vendor/bin/phpunit --verbose |   - vendor/bin/phpunit --verbose | ||||||
|  |  | ||||||
							
								
								
									
										113
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										113
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,10 +1,10 @@ | ||||||
| # PHP FFmpeg | # php-ffmpeg | ||||||
| 
 | 
 | ||||||
| [](http://travis-ci.org/PHP-FFMpeg/PHP-FFMpeg) | [](http://travis-ci.org/PHP-FFMpeg/PHP-FFMpeg) | ||||||
| 
 | 
 | ||||||
| [](https://insight.sensiolabs.com/projects/607f3111-e2d7-44e8-8bcc-54dd64521983) | [](https://insight.sensiolabs.com/projects/607f3111-e2d7-44e8-8bcc-54dd64521983) | ||||||
| 
 | 
 | ||||||
| An Object Oriented library to convert video/audio files with FFmpeg / AVConv. | An Object-Oriented library to convert video/audio files with FFmpeg / AVConv. | ||||||
| 
 | 
 | ||||||
| Check another amazing repo: [PHP FFMpeg extras](https://github.com/alchemy-fr/PHP-FFMpeg-Extras), you will find lots of Audio/Video formats there. | Check another amazing repo: [PHP FFMpeg extras](https://github.com/alchemy-fr/PHP-FFMpeg-Extras), you will find lots of Audio/Video formats there. | ||||||
| 
 | 
 | ||||||
|  | @ -16,7 +16,7 @@ This library requires a working FFMpeg install. You will need both FFMpeg and FF | ||||||
| Be sure that these binaries can be located with system PATH to get the benefit of the binary detection, | Be sure that these binaries can be located with system PATH to get the benefit of the binary detection, | ||||||
| otherwise you should have to explicitly give the binaries path on load. | otherwise you should have to explicitly give the binaries path on load. | ||||||
| 
 | 
 | ||||||
| For Windows users : Please find the binaries at http://ffmpeg.zeranoe.com/builds/. | For Windows users: Please find the binaries at http://ffmpeg.zeranoe.com/builds/. | ||||||
| 
 | 
 | ||||||
| ### Known issues: | ### Known issues: | ||||||
| 
 | 
 | ||||||
|  | @ -35,6 +35,9 @@ $ composer require php-ffmpeg/php-ffmpeg | ||||||
| ## Basic Usage | ## Basic Usage | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
|  | 
 | ||||||
|  | require 'vendor/autoload.php'; | ||||||
|  | 
 | ||||||
| $ffmpeg = FFMpeg\FFMpeg::create(); | $ffmpeg = FFMpeg\FFMpeg::create(); | ||||||
| $video = $ffmpeg->open('video.mpg'); | $video = $ffmpeg->open('video.mpg'); | ||||||
| $video | $video | ||||||
|  | @ -125,7 +128,7 @@ $video->save($format, 'video.avi'); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Transcoding progress can be monitored in realtime, see Format documentation | Transcoding progress can be monitored in realtime, see Format documentation | ||||||
| below for more informations. | below for more information. | ||||||
| 
 | 
 | ||||||
| ##### Extracting image | ##### Extracting image | ||||||
| 
 | 
 | ||||||
|  | @ -141,7 +144,7 @@ $frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(42)); | ||||||
| $frame->save('image.jpg'); | $frame->save('image.jpg'); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| If you want to extract multiple images from your video, you can use the following filter: | If you want to extract multiple images from the video, you can use the following filter: | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| $video | $video | ||||||
|  | @ -152,6 +155,38 @@ $video | ||||||
| $video | $video | ||||||
|     ->save(new FFMpeg\Format\Video\X264(), '/path/to/new/file'); |     ->save(new FFMpeg\Format\Video\X264(), '/path/to/new/file'); | ||||||
| ``` | ``` | ||||||
|  | By default, this will save the frames as `jpg` images. | ||||||
|  | 
 | ||||||
|  | You are able to override this using `setFrameFileType` to save the frames in another format: | ||||||
|  | ```php | ||||||
|  | $frameFileType = 'jpg'; // either 'jpg', 'jpeg' or 'png' | ||||||
|  | $filter = new ExtractMultipleFramesFilter($frameRate, $destinationFolder); | ||||||
|  | $filter->setFrameFileType($frameFileType); | ||||||
|  | 
 | ||||||
|  | $video->addFilter($filter); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ##### Clip | ||||||
|  | 
 | ||||||
|  | Cuts the video at a desired point. Use input seeking method. It is faster option than use filter clip. | ||||||
|  | 
 | ||||||
|  | ```php | ||||||
|  | $clip = $video->clip(FFMpeg\Coordinate\TimeCode::fromSeconds(30), FFMpeg\Coordinate\TimeCode::fromSeconds(15)); | ||||||
|  | $clip->save(new FFMpeg\Format\Video\X264(), 'video.avi'); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The clip filter takes two parameters: | ||||||
|  | 
 | ||||||
|  | - `$start`, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the start point of the clip | ||||||
|  | - `$duration`, optional, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the duration of the clip | ||||||
|  | 
 | ||||||
|  | On clip you can apply same filters as on video. For example resizing filter. | ||||||
|  | 
 | ||||||
|  | ```php | ||||||
|  | $clip = $video->clip(FFMpeg\Coordinate\TimeCode::fromSeconds(30), FFMpeg\Coordinate\TimeCode::fromSeconds(15)); | ||||||
|  | $clip->filters()->resize(new FFMpeg\Coordinate\Dimension(320, 240), FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_INSET, true); | ||||||
|  | $clip->save(new FFMpeg\Format\Video\X264(), 'video.avi'); | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| ##### Generate a waveform | ##### Generate a waveform | ||||||
| 
 | 
 | ||||||
|  | @ -159,13 +194,13 @@ You can generate a waveform of an audio file using the `FFMpeg\Media\Audio::wave | ||||||
| method. | method. | ||||||
| 
 | 
 | ||||||
| This code returns a `FFMpeg\Media\Waveform` instance. | This code returns a `FFMpeg\Media\Waveform` instance. | ||||||
| You can optionally pass dimensions as arguments, see dedicated | You can optionally pass dimensions as the first two arguments and an array of hex string colors for ffmpeg to use for the waveform, see dedicated | ||||||
| documentation below for more information. | documentation below for more information. | ||||||
| 
 | 
 | ||||||
| The ouput file MUST use the PNG extension. | The output file MUST use the PNG extension. | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| $waveform = $audio->waveform(640, 120); | $waveform = $audio->waveform(640, 120, array('#00FF00')); | ||||||
| $waveform->save('waveform.png'); | $waveform->save('waveform.png'); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | @ -178,8 +213,8 @@ $video = $ffmpeg->open( 'video.mp4' ); | ||||||
| // Set an audio format | // Set an audio format | ||||||
| $audio_format = new FFMpeg\Format\Audio\Mp3(); | $audio_format = new FFMpeg\Format\Audio\Mp3(); | ||||||
| 
 | 
 | ||||||
| // Extract the audio into a new file | // Extract the audio into a new file as mp3 | ||||||
| $video->save('audio.mp3'); | $video->save($audio_format, 'audio.mp3'); | ||||||
| 
 | 
 | ||||||
| // Set the audio file | // Set the audio file | ||||||
| $audio = $ffmpeg->open( 'audio.mp3' ); | $audio = $ffmpeg->open( 'audio.mp3' ); | ||||||
|  | @ -298,7 +333,7 @@ The framerate filter takes two parameters: | ||||||
| Synchronizes audio and video. | Synchronizes audio and video. | ||||||
| 
 | 
 | ||||||
| Some containers may use a delay that results in desynchronized outputs. This | Some containers may use a delay that results in desynchronized outputs. This | ||||||
| filters solves this issue. | filter solves this issue. | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| $video->filters()->synchronize(); | $video->filters()->synchronize(); | ||||||
|  | @ -317,6 +352,18 @@ The clip filter takes two parameters: | ||||||
| - `$start`, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the start point of the clip | - `$start`, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the start point of the clip | ||||||
| - `$duration`, optional, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the duration of the clip | - `$duration`, optional, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the duration of the clip | ||||||
| 
 | 
 | ||||||
|  | ###### Crop | ||||||
|  | 
 | ||||||
|  | Crops the video based on a width and height(a `Point`) | ||||||
|  | 
 | ||||||
|  | ```php | ||||||
|  | $video->filters()->crop(new FFMpeg\Coordinate\Point("t*100", 0, true), new FFMpeg\Coordinate\Dimension(200, 600)); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | It takes two parameters: | ||||||
|  | - `$point`, an instance of `FFMpeg\Coordinate\Point`, specifies the point to crop | ||||||
|  | - `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`, specifies the dimension of the output video | ||||||
|  | 
 | ||||||
| ### Audio | ### Audio | ||||||
| 
 | 
 | ||||||
| `FFMpeg\Media\Audio` can be transcoded too, ie: change codec, isolate audio or | `FFMpeg\Media\Audio` can be transcoded too, ie: change codec, isolate audio or | ||||||
|  | @ -346,7 +393,7 @@ $audio->save($format, 'track.flac'); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Transcoding progress can be monitored in realtime, see Format documentation | Transcoding progress can be monitored in realtime, see Format documentation | ||||||
| below for more informations. | below for more information. | ||||||
| 
 | 
 | ||||||
| ##### Filters | ##### Filters | ||||||
| 
 | 
 | ||||||
|  | @ -399,7 +446,7 @@ The resample filter takes two parameters : | ||||||
| 
 | 
 | ||||||
| #### Frame | #### Frame | ||||||
| 
 | 
 | ||||||
| A frame is a image at a timecode of a video ; see documentation above about | A frame is an image at a timecode of a video; see documentation above about | ||||||
| frame extraction. | frame extraction. | ||||||
| 
 | 
 | ||||||
| You can save frames using the `FFMpeg\Media\Frame::save` method. | You can save frames using the `FFMpeg\Media\Frame::save` method. | ||||||
|  | @ -409,7 +456,7 @@ $frame->save('target.jpg'); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| This method has a second optional boolean parameter. Set it to true to get | This method has a second optional boolean parameter. Set it to true to get | ||||||
| accurate images ; it takes more time to execute. | accurate images; it takes more time to execute. | ||||||
| 
 | 
 | ||||||
| #### Gif | #### Gif | ||||||
| 
 | 
 | ||||||
|  | @ -447,7 +494,7 @@ To concatenate videos encoded with the same codec, do as follow: | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| // In order to instantiate the video object, you HAVE TO pass a path to a valid video file. | // In order to instantiate the video object, you HAVE TO pass a path to a valid video file. | ||||||
| // We recommand that you put there the path of any of the video you want to use in this concatenation. | // We recommend that you put there the path of any of the video you want to use in this concatenation. | ||||||
| $video = $ffmpeg->open( '/path/to/video' ); | $video = $ffmpeg->open( '/path/to/video' ); | ||||||
| $video | $video | ||||||
|     ->concat(array('/path/to/video1', '/path/to/video2')) |     ->concat(array('/path/to/video1', '/path/to/video2')) | ||||||
|  | @ -460,7 +507,7 @@ To concatenate videos encoded with the same codec, do as follow: | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| // In order to instantiate the video object, you HAVE TO pass a path to a valid video file. | // In order to instantiate the video object, you HAVE TO pass a path to a valid video file. | ||||||
| // We recommand that you put there the path of any of the video you want to use in this concatenation. | // We recommend that you put there the path of any of the video you want to use in this concatenation. | ||||||
| $video = $ffmpeg->open( '/path/to/video' ); | $video = $ffmpeg->open( '/path/to/video' ); | ||||||
| 
 | 
 | ||||||
| $format = new FFMpeg\Format\Video\X264(); | $format = new FFMpeg\Format\Video\X264(); | ||||||
|  | @ -479,10 +526,10 @@ A format implements `FFMpeg\Format\FormatInterface`. To save to a video file, | ||||||
| use `FFMpeg\Format\VideoInterface`, and `FFMpeg\Format\AudioInterface` for | use `FFMpeg\Format\VideoInterface`, and `FFMpeg\Format\AudioInterface` for | ||||||
| audio files. | audio files. | ||||||
| 
 | 
 | ||||||
| Format can also extends `FFMpeg\Format\ProgressableInterface` to get realtime | A format can also extend `FFMpeg\Format\ProgressableInterface` to get realtime | ||||||
| informations about the transcoding. | information about the transcoding. | ||||||
| 
 | 
 | ||||||
| Predefined formats already provide progress informations as events. | Predefined formats already provide progress information as events. | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| $format = new FFMpeg\Format\Video\X264(); | $format = new FFMpeg\Format\Video\X264(); | ||||||
|  | @ -542,12 +589,12 @@ class CustomWMVFormat extends FFMpeg\Format\Video\DefaultVideo | ||||||
| 
 | 
 | ||||||
| #### Coordinates | #### Coordinates | ||||||
| 
 | 
 | ||||||
| FFMpeg use many units for time and space coordinates. | FFMpeg uses many units for time and space coordinates. | ||||||
| 
 | 
 | ||||||
| - `FFMpeg\Coordinate\AspectRatio` represents an aspect ratio. | - `FFMpeg\Coordinate\AspectRatio` represents an aspect ratio. | ||||||
| - `FFMpeg\Coordinate\Dimension` represent a dimension. | - `FFMpeg\Coordinate\Dimension` represent a dimension. | ||||||
| - `FFMpeg\Coordinate\FrameRate` represent a framerate. | - `FFMpeg\Coordinate\FrameRate` represent a framerate. | ||||||
| - `FFMpeg\Coordinate\Point` represent a point. | - `FFMpeg\Coordinate\Point` represent a point. (Supports dynamic points since v0.10.0) | ||||||
| - `FFMpeg\Coordinate\TimeCode` represent a timecode. | - `FFMpeg\Coordinate\TimeCode` represent a timecode. | ||||||
| 
 | 
 | ||||||
| ### FFProbe | ### FFProbe | ||||||
|  | @ -571,9 +618,19 @@ $ffprobe | ||||||
|     ->get('duration');             // returns the duration property |     ->get('duration');             // returns the duration property | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | ### Validating media files | ||||||
|  | 
 | ||||||
|  | (since 0.10.0) | ||||||
|  | You can validate media files using PHP-FFMpeg's FFProbe wrapper. | ||||||
|  | 
 | ||||||
|  | ```php | ||||||
|  | $ffprobe = FFMpeg\FFProbe::create(); | ||||||
|  | $ffprobe->isValid('/path/to/file/to/check'); // returns bool | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| ## Using with Silex Microframework | ## Using with Silex Microframework | ||||||
| 
 | 
 | ||||||
| Service provider is easy to set up: | The service provider is easy to set up: | ||||||
| 
 | 
 | ||||||
| ```php | ```php | ||||||
| $app = new Silex\Application(); | $app = new Silex\Application(); | ||||||
|  | @ -597,10 +654,14 @@ $app->register(new FFMpeg\FFMpegServiceProvider(), array( | ||||||
| )); | )); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## API Browser |  | ||||||
| 
 |  | ||||||
| Browse the [API](https://ffmpeg-php.readthedocs.io/en/latest/_static/API/) |  | ||||||
| 
 |  | ||||||
| ## License | ## License | ||||||
| 
 | 
 | ||||||
| This project is licensed under the [MIT license](http://opensource.org/licenses/MIT). | This project is licensed under the [MIT license](http://opensource.org/licenses/MIT). | ||||||
|  | 
 | ||||||
|  | Music: "Favorite Secrets" by Waylon Thornton | ||||||
|  | From the Free Music Archive | ||||||
|  | [CC BY NC SA](http://creativecommons.org/licenses/by-nc-sa/3.0/us/) | ||||||
|  | 
 | ||||||
|  | Music: "Siesta" by Jahzzar | ||||||
|  | From the Free Music Archive | ||||||
|  | [CC BY SA](https://creativecommons.org/licenses/by-sa/3.0/) | ||||||
|  |  | ||||||
|  | @ -24,11 +24,16 @@ | ||||||
|             "name": "Romain Biard", |             "name": "Romain Biard", | ||||||
|             "email": "romain.biard@gmail.com", |             "email": "romain.biard@gmail.com", | ||||||
|             "homepage": "https://www.strime.io/" |             "homepage": "https://www.strime.io/" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "name": "Jens Hausdorf", | ||||||
|  |             "email": "hello@jens-hausdorf.de", | ||||||
|  |             "homepage": "https://jens-hausdorf.de" | ||||||
|         } |         } | ||||||
|     ], |     ], | ||||||
|     "require": { |     "require": { | ||||||
|         "php": "^5.3.9 || ^7.0", |         "php": "^5.3.9 || ^7.0", | ||||||
|         "alchemy/binary-driver": "^1.5", |         "alchemy/binary-driver": "^1.5 || ~2.0.0 || ^5.0", | ||||||
|         "doctrine/cache": "^1.0", |         "doctrine/cache": "^1.0", | ||||||
|         "evenement/evenement": "^3.0 || ^2.0 || ^1.0", |         "evenement/evenement": "^3.0 || ^2.0 || ^1.0", | ||||||
|         "neutron/temporary-filesystem": "^2.1.1" |         "neutron/temporary-filesystem": "^2.1.1" | ||||||
|  | @ -39,7 +44,7 @@ | ||||||
|     "require-dev": { |     "require-dev": { | ||||||
|         "sami/sami": "~1.0", |         "sami/sami": "~1.0", | ||||||
|         "silex/silex": "~1.0", |         "silex/silex": "~1.0", | ||||||
|         "phpunit/phpunit": "^4.8" |         "phpunit/phpunit": "^4.8.36" | ||||||
|     }, |     }, | ||||||
|     "autoload": { |     "autoload": { | ||||||
|         "psr-0": { |         "psr-0": { | ||||||
|  |  | ||||||
|  | @ -21,6 +21,12 @@ class AspectRatio | ||||||
|     // named 16:9 or 1.77:1 HD video standard
 |     // named 16:9 or 1.77:1 HD video standard
 | ||||||
|     const AR_16_9 = '16/9'; |     const AR_16_9 = '16/9'; | ||||||
|      |      | ||||||
|  |     // named 8:5 or 16:10 or 1.6:1
 | ||||||
|  |     const AR_8_5 = '8/5'; | ||||||
|  | 
 | ||||||
|  |     // named 25:16 or 1.56:1
 | ||||||
|  |     const AR_25_16 = '25/16'; | ||||||
|  | 
 | ||||||
|     // named 3:2 or 1.5:1 see http://en.wikipedia.org/wiki/135_film
 |     // named 3:2 or 1.5:1 see http://en.wikipedia.org/wiki/135_film
 | ||||||
|     const AR_3_2 = '3/2'; |     const AR_3_2 = '3/2'; | ||||||
|     // named 5:3 or 1.66:1 see http://en.wikipedia.org/wiki/Super_16_mm
 |     // named 5:3 or 1.66:1 see http://en.wikipedia.org/wiki/Super_16_mm
 | ||||||
|  | @ -160,6 +166,10 @@ class AspectRatio | ||||||
|                 return 4 / 3; |                 return 4 / 3; | ||||||
|             case static::AR_16_9: |             case static::AR_16_9: | ||||||
|                 return 16 / 9; |                 return 16 / 9; | ||||||
|  |             case static::AR_8_5: | ||||||
|  |                 return 8 / 5; | ||||||
|  |             case static::AR_25_16: | ||||||
|  |                 return 25 / 16; | ||||||
|             case static::AR_1_1: |             case static::AR_1_1: | ||||||
|                 return 1 / 1; |                 return 1 / 1; | ||||||
|             case static::AR_1_DOT_85_1: |             case static::AR_1_DOT_85_1: | ||||||
|  | @ -207,6 +217,8 @@ class AspectRatio | ||||||
|         $availables = array( |         $availables = array( | ||||||
|             static::AR_4_3 => static::valueFromName(static::AR_4_3), |             static::AR_4_3 => static::valueFromName(static::AR_4_3), | ||||||
|             static::AR_16_9 => static::valueFromName(static::AR_16_9), |             static::AR_16_9 => static::valueFromName(static::AR_16_9), | ||||||
|  |             static::AR_8_5 => static::valueFromName(static::AR_8_5), | ||||||
|  |             static::AR_25_16 => static::valueFromName(static::AR_25_16), | ||||||
|             static::AR_1_1 => static::valueFromName(static::AR_1_1), |             static::AR_1_1 => static::valueFromName(static::AR_1_1), | ||||||
|             static::AR_1_DOT_85_1 => static::valueFromName(static::AR_1_DOT_85_1), |             static::AR_1_DOT_85_1 => static::valueFromName(static::AR_1_DOT_85_1), | ||||||
|             static::AR_2_DOT_39_1 => static::valueFromName(static::AR_2_DOT_39_1), |             static::AR_2_DOT_39_1 => static::valueFromName(static::AR_2_DOT_39_1), | ||||||
|  |  | ||||||
|  | @ -16,10 +16,15 @@ class Point | ||||||
|     private $x; |     private $x; | ||||||
|     private $y; |     private $y; | ||||||
| 
 | 
 | ||||||
|     public function __construct($x, $y) |     public function __construct($x, $y, $dynamic = false) | ||||||
|     { |     { | ||||||
|         $this->x = (int) $x; |         if ($dynamic) { | ||||||
|         $this->y = (int) $y; |             $this->x = $x; | ||||||
|  |             $this->y = $y; | ||||||
|  |         } else { | ||||||
|  |             $this->x = (int)$x; | ||||||
|  |             $this->y = (int)$y; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -170,6 +170,25 @@ class FFProbe | ||||||
|         return $this->probe($pathfile, '-show_format', static::TYPE_FORMAT); |         return $this->probe($pathfile, '-show_format', static::TYPE_FORMAT); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @api | ||||||
|  |      * | ||||||
|  |      * Checks wether the given `$pathfile` is considered a valid media file. | ||||||
|  |      * | ||||||
|  |      * @param string $pathfile | ||||||
|  |      * @return bool | ||||||
|  |      * @since 0.10.0 | ||||||
|  |      */ | ||||||
|  |     public function isValid($pathfile) | ||||||
|  |     { | ||||||
|  |         try { | ||||||
|  |             return $this->format($pathfile)->get('duration') > 0; | ||||||
|  |         } catch(\Exception $e) { | ||||||
|  |             // complete invalid data
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @api |      * @api | ||||||
|      * |      * | ||||||
|  |  | ||||||
|  | @ -71,4 +71,18 @@ class AudioFilters | ||||||
| 
 | 
 | ||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Applies a custom filter | ||||||
|  |      * | ||||||
|  |      * @param string    $parameters | ||||||
|  |      * | ||||||
|  |      * @return AudioFilters | ||||||
|  |      */ | ||||||
|  |     public function custom($parameters) | ||||||
|  |     { | ||||||
|  |         $this->media->addFilter(new CustomFilter($parameters)); | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										52
									
								
								src/FFMpeg/Filters/Audio/CustomFilter.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/FFMpeg/Filters/Audio/CustomFilter.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * This file is part of PHP-FFmpeg. | ||||||
|  |  * | ||||||
|  |  * (c) Alchemy <dev.team@alchemy.fr> | ||||||
|  |  * | ||||||
|  |  * For the full copyright and license information, please view the LICENSE | ||||||
|  |  * file that was distributed with this source code. | ||||||
|  |  */ | ||||||
|  | namespace FFMpeg\Filters\Audio; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Format\AudioInterface; | ||||||
|  | use FFMpeg\Media\Audio; | ||||||
|  | 
 | ||||||
|  | class CustomFilter implements AudioFilterInterface | ||||||
|  | { | ||||||
|  |     /** @var string */ | ||||||
|  |     private $filter; | ||||||
|  |     /** @var integer */ | ||||||
|  |     private $priority; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * A custom filter, useful if you want to build complex filters | ||||||
|  |      * | ||||||
|  |      * @param string $filter | ||||||
|  |      * @param int    $priority | ||||||
|  |      */ | ||||||
|  |     public function __construct($filter, $priority = 0) | ||||||
|  |     { | ||||||
|  |         $this->filter = $filter; | ||||||
|  |         $this->priority = $priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public function getPriority() | ||||||
|  |     { | ||||||
|  |         return $this->priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public function apply(Audio $audio, AudioInterface $format) | ||||||
|  |     { | ||||||
|  |         $commands = array('-af', $this->filter); | ||||||
|  | 
 | ||||||
|  |         return $commands; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/FFMpeg/Filters/Frame/CustomFrameFilter.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/FFMpeg/Filters/Frame/CustomFrameFilter.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * This file is part of PHP-FFmpeg. | ||||||
|  |  * | ||||||
|  |  * (c) Alchemy <dev.team@alchemy.fr> | ||||||
|  |  * | ||||||
|  |  * For the full copyright and license information, please view the LICENSE | ||||||
|  |  * file that was distributed with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace FFMpeg\Filters\Frame; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Exception\RuntimeException; | ||||||
|  | use FFMpeg\Media\Frame; | ||||||
|  | 
 | ||||||
|  | class CustomFrameFilter implements FrameFilterInterface | ||||||
|  | { | ||||||
|  |     /** @var string */ | ||||||
|  |     private $filter; | ||||||
|  |     /** @var integer */ | ||||||
|  |     private $priority; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * A custom filter, useful if you want to build complex filters | ||||||
|  |      * | ||||||
|  |      * @param string $filter | ||||||
|  |      * @param int    $priority | ||||||
|  |      */ | ||||||
|  |     public function __construct($filter, $priority = 0) | ||||||
|  |     { | ||||||
|  |         $this->filter = $filter; | ||||||
|  |         $this->priority = $priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public function getPriority() | ||||||
|  |     { | ||||||
|  |         return $this->priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public function apply(Frame $frame) | ||||||
|  |     { | ||||||
|  |         $commands = array('-vf', $this->filter); | ||||||
|  | 
 | ||||||
|  |         return $commands; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -36,4 +36,18 @@ class FrameFilters | ||||||
| 
 | 
 | ||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Applies a custom filter: -vf foo bar | ||||||
|  |      * | ||||||
|  |      * @param string    $parameters | ||||||
|  |      * | ||||||
|  |      * @return FrameFilters | ||||||
|  |      */ | ||||||
|  |     public function custom($parameters) | ||||||
|  |     { | ||||||
|  |         $this->frame->addFilter(new CustomFrameFilter($parameters)); | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -35,6 +35,10 @@ class ExtractMultipleFramesFilter implements VideoFilterInterface | ||||||
|     private $priority; |     private $priority; | ||||||
|     private $frameRate; |     private $frameRate; | ||||||
|     private $destinationFolder; |     private $destinationFolder; | ||||||
|  |     private $frameFileType = 'jpg'; | ||||||
|  | 
 | ||||||
|  |     /** @var array */ | ||||||
|  |     private static $supportedFrameFileTypes = ['jpg', 'jpeg', 'png']; | ||||||
| 
 | 
 | ||||||
|     public function __construct($frameRate = self::FRAMERATE_EVERY_SEC, $destinationFolder = __DIR__, $priority = 0) |     public function __construct($frameRate = self::FRAMERATE_EVERY_SEC, $destinationFolder = __DIR__, $priority = 0) | ||||||
|     { |     { | ||||||
|  | @ -49,6 +53,20 @@ class ExtractMultipleFramesFilter implements VideoFilterInterface | ||||||
|         $this->destinationFolder = $destinationFolder; |         $this->destinationFolder = $destinationFolder; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param string $frameFileType | ||||||
|  | 	 * @throws \FFMpeg\Exception\InvalidArgumentException | ||||||
|  | 	 * @return ExtractMultipleFramesFilter | ||||||
|  | 	 */ | ||||||
|  |     public function setFrameFileType($frameFileType) { | ||||||
|  |     	if (in_array($frameFileType, self::$supportedFrameFileTypes)) { | ||||||
|  |     		$this->frameFileType = $frameFileType; | ||||||
|  |     		return $this; | ||||||
|  |     	} | ||||||
|  | 
 | ||||||
|  |     	throw new InvalidArgumentException('Invalid frame file type, use: ' . implode(',', self::$supportedFrameFileTypes)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|  | @ -117,7 +135,7 @@ class ExtractMultipleFramesFilter implements VideoFilterInterface | ||||||
|             // Set the parameters
 |             // Set the parameters
 | ||||||
|             $commands[] = '-vf'; |             $commands[] = '-vf'; | ||||||
|             $commands[] = 'fps=' . $this->frameRate; |             $commands[] = 'fps=' . $this->frameRate; | ||||||
|             $commands[] = $this->destinationFolder . 'frame-%'.$nbDigitsInFileNames.'d.jpg'; |             $commands[] = $this->destinationFolder . 'frame-%'.$nbDigitsInFileNames.'d.' . $this->frameFileType; | ||||||
|         } |         } | ||||||
|         catch (RuntimeException $e) { |         catch (RuntimeException $e) { | ||||||
|             throw new RuntimeException('An error occured while extracting the frames: ' . $e->getMessage() . '. The code: ' . $e->getCode()); |             throw new RuntimeException('An error occured while extracting the frames: ' . $e->getMessage() . '. The code: ' . $e->getCode()); | ||||||
|  |  | ||||||
|  | @ -121,10 +121,10 @@ abstract class DefaultAudio extends EventEmitter implements AudioInterface, Prog | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total) |     public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total, $duration = 0) | ||||||
|     { |     { | ||||||
|         $format = $this; |         $format = $this; | ||||||
|         $listener = new AudioProgressListener($ffprobe, $media->getPathfile(), $pass, $total); |         $listener = new AudioProgressListener($ffprobe, $media->getPathfile(), $pass, $total, $duration); | ||||||
|         $listener->on('progress', function () use ($media, $format) { |         $listener->on('progress', function () use ($media, $format) { | ||||||
|            $format->emit('progress', array_merge(array($media, $format), func_get_args())); |            $format->emit('progress', array_merge(array($media, $format), func_get_args())); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  | @ -80,12 +80,13 @@ abstract class AbstractProgressListener extends EventEmitter implements Listener | ||||||
|      * |      * | ||||||
|      * @throws RuntimeException |      * @throws RuntimeException | ||||||
|      */ |      */ | ||||||
|     public function __construct(FFProbe $ffprobe, $pathfile, $currentPass, $totalPass) |     public function __construct(FFProbe $ffprobe, $pathfile, $currentPass, $totalPass, $duration = 0) | ||||||
|     { |     { | ||||||
|         $this->ffprobe = $ffprobe; |         $this->ffprobe = $ffprobe; | ||||||
|         $this->pathfile = $pathfile; |         $this->pathfile = $pathfile; | ||||||
|         $this->currentPass = $currentPass; |         $this->currentPass = $currentPass; | ||||||
|         $this->totalPass = $totalPass; |         $this->totalPass = $totalPass; | ||||||
|  |         $this->duration = $duration; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -254,9 +255,8 @@ abstract class AbstractProgressListener extends EventEmitter implements Listener | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $this->totalSize = $format->get('size') / 1024; |         $this->duration = (int) $this->duration > 0 ? $this->duration : $format->get('duration'); | ||||||
|         $this->duration = $format->get('duration'); |         $this->totalSize = $format->get('size') / 1024 * ($this->duration / $format->get('duration')); | ||||||
| 
 |  | ||||||
|         $this->initialized = true; |         $this->initialized = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,8 +24,9 @@ interface ProgressableInterface extends EventEmitterInterface | ||||||
|      * @param FFProbe            $ffprobe |      * @param FFProbe            $ffprobe | ||||||
|      * @param Integer            $pass    The current pas snumber |      * @param Integer            $pass    The current pas snumber | ||||||
|      * @param Integer            $total   The total pass number |      * @param Integer            $total   The total pass number | ||||||
|  |      * @param Integer            $duration   The new video duration | ||||||
|      * |      * | ||||||
|      * @return array An array of listeners |      * @return array An array of listeners | ||||||
|      */ |      */ | ||||||
|     public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total); |     public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total, $duration = 0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -125,10 +125,10 @@ abstract class DefaultVideo extends DefaultAudio implements VideoInterface | ||||||
|     /** |     /** | ||||||
|      * {@inheritdoc} |      * {@inheritdoc} | ||||||
|      */ |      */ | ||||||
|     public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total) |     public function createProgressListener(MediaTypeInterface $media, FFProbe $ffprobe, $pass, $total, $duration = 0) | ||||||
|     { |     { | ||||||
|         $format = $this; |         $format = $this; | ||||||
|         $listeners = array(new VideoProgressListener($ffprobe, $media->getPathfile(), $pass, $total)); |         $listeners = array(new VideoProgressListener($ffprobe, $media->getPathfile(), $pass, $total, $duration)); | ||||||
| 
 | 
 | ||||||
|         foreach ($listeners as $listener) { |         foreach ($listeners as $listener) { | ||||||
|             $listener->on('progress', function () use ($format, $media) { |             $listener->on('progress', function () use ($format, $media) { | ||||||
|  |  | ||||||
|  | @ -52,6 +52,6 @@ class WebM extends DefaultVideo | ||||||
|      */ |      */ | ||||||
|     public function getAvailableVideoCodecs() |     public function getAvailableVideoCodecs() | ||||||
|     { |     { | ||||||
|         return array('libvpx'); |         return array('libvpx', 'libvpx-vp9'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										292
									
								
								src/FFMpeg/Media/AbstractVideo.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								src/FFMpeg/Media/AbstractVideo.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,292 @@ | ||||||
|  | <?php | ||||||
|  | /* | ||||||
|  |  * This file is part of PHP-FFmpeg. | ||||||
|  |  * | ||||||
|  |  * (c) Alchemy <info@alchemy.fr> | ||||||
|  |  * | ||||||
|  |  * For the full copyright and license information, please view the LICENSE | ||||||
|  |  * file that was distributed with this source code. | ||||||
|  |  */ | ||||||
|  | namespace FFMpeg\Media; | ||||||
|  | 
 | ||||||
|  | use Alchemy\BinaryDriver\Exception\ExecutionFailureException; | ||||||
|  | use FFMpeg\Filters\Audio\SimpleFilter; | ||||||
|  | use FFMpeg\Exception\InvalidArgumentException; | ||||||
|  | use FFMpeg\Exception\RuntimeException; | ||||||
|  | use FFMpeg\Filters\Video\VideoFilters; | ||||||
|  | use FFMpeg\Filters\FilterInterface; | ||||||
|  | use FFMpeg\Format\FormatInterface; | ||||||
|  | use FFMpeg\Format\ProgressableInterface; | ||||||
|  | use FFMpeg\Format\AudioInterface; | ||||||
|  | use FFMpeg\Format\VideoInterface; | ||||||
|  | use Neutron\TemporaryFilesystem\Manager as FsManager; | ||||||
|  | use FFMpeg\Filters\Video\ClipFilter; | ||||||
|  | 
 | ||||||
|  | abstract class AbstractVideo extends Audio | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * FileSystem Manager instance | ||||||
|  |      * @var Manager | ||||||
|  |      */ | ||||||
|  |     protected $fs; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * FileSystem Manager ID | ||||||
|  |      * @var int | ||||||
|  |      */ | ||||||
|  |     protected $fsId; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritDoc | ||||||
|  |      * @return VideoFilters | ||||||
|  |      */ | ||||||
|  |     public function filters() | ||||||
|  |     { | ||||||
|  |         return new VideoFilters($this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @inheritDoc | ||||||
|  |      * @return Video | ||||||
|  |      */ | ||||||
|  |     public function addFilter(FilterInterface $filter) | ||||||
|  |     { | ||||||
|  |         $this->filters->add($filter); | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Exports the video in the desired format, applies registered filters. | ||||||
|  |      * | ||||||
|  |      * @param FormatInterface   $format | ||||||
|  |      * @param string            $outputPathfile | ||||||
|  |      * @return Video | ||||||
|  |      * @throws RuntimeException | ||||||
|  |      */ | ||||||
|  |     public function save(FormatInterface $format, $outputPathfile) | ||||||
|  |     { | ||||||
|  |         $passes = $this->buildCommand($format, $outputPathfile); | ||||||
|  | 
 | ||||||
|  |         $failure = null; | ||||||
|  |         $totalPasses = $format->getPasses(); | ||||||
|  | 
 | ||||||
|  |         foreach ($passes as $pass => $passCommands) { | ||||||
|  |             try { | ||||||
|  |                 /** add listeners here */ | ||||||
|  |                 $listeners = null; | ||||||
|  | 
 | ||||||
|  |                 if ($format instanceof ProgressableInterface) { | ||||||
|  |                     $filters = clone $this->filters; | ||||||
|  |                     $duration = 0; | ||||||
|  | 
 | ||||||
|  |                     // check the filters of the video, and if the video has the ClipFilter then
 | ||||||
|  |                     // take the new video duration and send to the
 | ||||||
|  |                     // FFMpeg\Format\ProgressListener\AbstractProgressListener class
 | ||||||
|  |                     foreach ($filters as $filter) { | ||||||
|  |                         if ($filter instanceof ClipFilter) { | ||||||
|  |                             $duration = $filter->getDuration()->toSeconds(); | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     $listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses, $duration); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 $this->driver->command($passCommands, false, $listeners); | ||||||
|  |             } catch (ExecutionFailureException $e) { | ||||||
|  |                 $failure = $e; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->fs->clean($this->fsId); | ||||||
|  | 
 | ||||||
|  |         if (null !== $failure) { | ||||||
|  |             throw new RuntimeException('Encoding failed', $failure->getCode(), $failure); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * NOTE: This method is different to the Audio's one, because Video is using passes. | ||||||
|  |      * @inheritDoc | ||||||
|  |      */ | ||||||
|  |     public function getFinalCommand(FormatInterface $format, $outputPathfile) | ||||||
|  |     { | ||||||
|  |         $finalCommands = array(); | ||||||
|  | 
 | ||||||
|  |         foreach ($this->buildCommand($format, $outputPathfile) as $pass => $passCommands) { | ||||||
|  |             $finalCommands[] = implode(' ', $passCommands); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->fs->clean($this->fsId); | ||||||
|  | 
 | ||||||
|  |         return $finalCommands; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * **NOTE:** This creates passes instead of a single command! | ||||||
|  |      * | ||||||
|  |      * @inheritDoc | ||||||
|  |      * @return string[][] | ||||||
|  |      */ | ||||||
|  |     protected function buildCommand(FormatInterface $format, $outputPathfile) | ||||||
|  |     { | ||||||
|  |         $commands = $this->basePartOfCommand(); | ||||||
|  | 
 | ||||||
|  |         $filters = clone $this->filters; | ||||||
|  |         $filters->add(new SimpleFilter($format->getExtraParams(), 10)); | ||||||
|  | 
 | ||||||
|  |         if ($this->driver->getConfiguration()->has('ffmpeg.threads')) { | ||||||
|  |             $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')))); | ||||||
|  |         } | ||||||
|  |         if ($format instanceof VideoInterface) { | ||||||
|  |             if (null !== $format->getVideoCodec()) { | ||||||
|  |                 $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec()))); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if ($format instanceof AudioInterface) { | ||||||
|  |             if (null !== $format->getAudioCodec()) { | ||||||
|  |                 $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec()))); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         foreach ($filters as $filter) { | ||||||
|  |             $commands = array_merge($commands, $filter->apply($this, $format)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($format instanceof VideoInterface) { | ||||||
|  |             $commands[] = '-b:v'; | ||||||
|  |             $commands[] = $format->getKiloBitrate() . 'k'; | ||||||
|  |             $commands[] = '-refs'; | ||||||
|  |             $commands[] = '6'; | ||||||
|  |             $commands[] = '-coder'; | ||||||
|  |             $commands[] = '1'; | ||||||
|  |             $commands[] = '-sc_threshold'; | ||||||
|  |             $commands[] = '40'; | ||||||
|  |             $commands[] = '-flags'; | ||||||
|  |             $commands[] = '+loop'; | ||||||
|  |             $commands[] = '-me_range'; | ||||||
|  |             $commands[] = '16'; | ||||||
|  |             $commands[] = '-subq'; | ||||||
|  |             $commands[] = '7'; | ||||||
|  |             $commands[] = '-i_qfactor'; | ||||||
|  |             $commands[] = '0.71'; | ||||||
|  |             $commands[] = '-qcomp'; | ||||||
|  |             $commands[] = '0.6'; | ||||||
|  |             $commands[] = '-qdiff'; | ||||||
|  |             $commands[] = '4'; | ||||||
|  |             $commands[] = '-trellis'; | ||||||
|  |             $commands[] = '1'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ($format instanceof AudioInterface) { | ||||||
|  |             if (null !== $format->getAudioKiloBitrate()) { | ||||||
|  |                 $commands[] = '-b:a'; | ||||||
|  |                 $commands[] = $format->getAudioKiloBitrate() . 'k'; | ||||||
|  |             } | ||||||
|  |             if (null !== $format->getAudioChannels()) { | ||||||
|  |                 $commands[] = '-ac'; | ||||||
|  |                 $commands[] = $format->getAudioChannels(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // If the user passed some additional parameters
 | ||||||
|  |         if ($format instanceof VideoInterface) { | ||||||
|  |             if (null !== $format->getAdditionalParameters()) { | ||||||
|  |                 foreach ($format->getAdditionalParameters() as $additionalParameter) { | ||||||
|  |                     $commands[] = $additionalParameter; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Merge Filters into one command
 | ||||||
|  |         $videoFilterVars = $videoFilterProcesses = array(); | ||||||
|  |         for ($i = 0; $i < count($commands); $i++) { | ||||||
|  |             $command = $commands[$i]; | ||||||
|  |             if ($command === '-vf') { | ||||||
|  |                 $commandSplits = explode(";", $commands[$i + 1]); | ||||||
|  |                 if (count($commandSplits) == 1) { | ||||||
|  |                     $commandSplit = $commandSplits[0]; | ||||||
|  |                     $command = trim($commandSplit); | ||||||
|  |                     if (preg_match("/^\[in\](.*?)\[out\]$/is", $command, $match)) { | ||||||
|  |                         $videoFilterProcesses[] = $match[1]; | ||||||
|  |                     } else { | ||||||
|  |                         $videoFilterProcesses[] = $command; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     foreach ($commandSplits as $commandSplit) { | ||||||
|  |                         $command = trim($commandSplit); | ||||||
|  |                         if (preg_match("/^\[[^\]]+\](.*?)\[[^\]]+\]$/is", $command, $match)) { | ||||||
|  |                             $videoFilterProcesses[] = $match[1]; | ||||||
|  |                         } else { | ||||||
|  |                             $videoFilterVars[] = $command; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 unset($commands[$i]); | ||||||
|  |                 unset($commands[$i + 1]); | ||||||
|  |                 $i++; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         $videoFilterCommands = $videoFilterVars; | ||||||
|  |         $lastInput = 'in'; | ||||||
|  |         foreach ($videoFilterProcesses as $i => $process) { | ||||||
|  |             $command = '[' . $lastInput . ']'; | ||||||
|  |             $command .= $process; | ||||||
|  |             $lastInput = 'p' . $i; | ||||||
|  |             if ($i === (count($videoFilterProcesses) - 1)) { | ||||||
|  |                 $command .= '[out]'; | ||||||
|  |             } else { | ||||||
|  |                 $command .= '[' . $lastInput . ']'; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $videoFilterCommands[] = $command; | ||||||
|  |         } | ||||||
|  |         $videoFilterCommand = implode(';', $videoFilterCommands); | ||||||
|  | 
 | ||||||
|  |         if ($videoFilterCommand) { | ||||||
|  |             $commands[] = '-vf'; | ||||||
|  |             $commands[] = $videoFilterCommand; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->fs = FsManager::create(); | ||||||
|  |         $this->fsId = uniqid('ffmpeg-passes'); | ||||||
|  |         $passPrefix = $this->fs->createTemporaryDirectory(0777, 50, $this->fsId) . '/' . uniqid('pass-'); | ||||||
|  |         $passes = array(); | ||||||
|  |         $totalPasses = $format->getPasses(); | ||||||
|  | 
 | ||||||
|  |         if (!$totalPasses) { | ||||||
|  |             throw new InvalidArgumentException('Pass number should be a positive value.'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for ($i = 1; $i <= $totalPasses; $i++) { | ||||||
|  |             $pass = $commands; | ||||||
|  | 
 | ||||||
|  |             if ($totalPasses > 1) { | ||||||
|  |                 $pass[] = '-pass'; | ||||||
|  |                 $pass[] = $i; | ||||||
|  |                 $pass[] = '-passlogfile'; | ||||||
|  |                 $pass[] = $passPrefix; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $pass[] = $outputPathfile; | ||||||
|  | 
 | ||||||
|  |             $passes[] = $pass; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $passes; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Return base part of command. | ||||||
|  |      * | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     protected function basePartOfCommand() | ||||||
|  |     { | ||||||
|  |         return array('-y', '-i', $this->pathfile); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -54,9 +54,7 @@ class Audio extends AbstractStreamableMedia | ||||||
|      * |      * | ||||||
|      * @param FormatInterface   $format |      * @param FormatInterface   $format | ||||||
|      * @param string            $outputPathfile |      * @param string            $outputPathfile | ||||||
|      * |  | ||||||
|      * @return Audio |      * @return Audio | ||||||
|      * |  | ||||||
|      * @throws RuntimeException |      * @throws RuntimeException | ||||||
|      */ |      */ | ||||||
|     public function save(FormatInterface $format, $outputPathfile) |     public function save(FormatInterface $format, $outputPathfile) | ||||||
|  | @ -64,9 +62,42 @@ class Audio extends AbstractStreamableMedia | ||||||
|         $listeners = null; |         $listeners = null; | ||||||
| 
 | 
 | ||||||
|         if ($format instanceof ProgressableInterface) { |         if ($format instanceof ProgressableInterface) { | ||||||
|             $listeners = $format->createProgressListener($this, $this->ffprobe, 1, 1); |             $listeners = $format->createProgressListener($this, $this->ffprobe, 1, 1, 0); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         $commands = $this->buildCommand($format, $outputPathfile); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             $this->driver->command($commands, false, $listeners); | ||||||
|  |         } catch (ExecutionFailureException $e) { | ||||||
|  |             $this->cleanupTemporaryFile($outputPathfile); | ||||||
|  |             throw new RuntimeException('Encoding failed', $e->getCode(), $e); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the final command as a string, useful for debugging purposes. | ||||||
|  |      * | ||||||
|  |      * @param FormatInterface   $format | ||||||
|  |      * @param string            $outputPathfile | ||||||
|  |      * @return string | ||||||
|  |      * @since 0.11.0 | ||||||
|  |      */ | ||||||
|  |     public function getFinalCommand(FormatInterface $format, $outputPathfile) { | ||||||
|  |         return implode(' ', $this->buildCommand($format, $outputPathfile)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Builds the command which will be executed with the provided format | ||||||
|  |      * | ||||||
|  |      * @param FormatInterface   $format | ||||||
|  |      * @param string            $outputPathfile | ||||||
|  |      * @return string[] An array which are the components of the command | ||||||
|  |      * @since 0.11.0 | ||||||
|  |      */ | ||||||
|  |     protected function buildCommand(FormatInterface $format, $outputPathfile) { | ||||||
|         $commands = array('-y', '-i', $this->pathfile); |         $commands = array('-y', '-i', $this->pathfile); | ||||||
| 
 | 
 | ||||||
|         $filters = clone $this->filters; |         $filters = clone $this->filters; | ||||||
|  | @ -93,14 +124,7 @@ class Audio extends AbstractStreamableMedia | ||||||
|         } |         } | ||||||
|         $commands[] = $outputPathfile; |         $commands[] = $outputPathfile; | ||||||
| 
 | 
 | ||||||
|         try { |         return $commands; | ||||||
|             $this->driver->command($commands, false, $listeners); |  | ||||||
|         } catch (ExecutionFailureException $e) { |  | ||||||
|             $this->cleanupTemporaryFile($outputPathfile); |  | ||||||
|             throw new RuntimeException('Encoding failed', $e->getCode(), $e); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -108,10 +132,22 @@ class Audio extends AbstractStreamableMedia | ||||||
|      * |      * | ||||||
|      * @param  integer $width |      * @param  integer $width | ||||||
|      * @param  integer $height |      * @param  integer $height | ||||||
|  |      * @param array $colors Array of colors for ffmpeg to use. Color format is #000000 (RGB hex string with #)
 | ||||||
|      * @return Waveform |      * @return Waveform | ||||||
|      */ |      */ | ||||||
|     public function waveform($width = 640, $height = 120) |     public function waveform($width = 640, $height = 120, $colors = array(Waveform::DEFAULT_COLOR)) | ||||||
|     { |     { | ||||||
|         return new Waveform($this, $this->driver, $this->ffprobe, $width, $height); |         return new Waveform($this, $this->driver, $this->ffprobe, $width, $height, $colors); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Concatenates a list of audio files into one unique audio file. | ||||||
|  |      * | ||||||
|  |      * @param  array $sources | ||||||
|  |      * @return Concat | ||||||
|  |      */ | ||||||
|  |     public function concat($sources) | ||||||
|  |     { | ||||||
|  |         return new Concat($sources, $this->driver, $this->ffprobe); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										60
									
								
								src/FFMpeg/Media/Clip.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/FFMpeg/Media/Clip.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | <?php | ||||||
|  | namespace FFMpeg\Media; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Driver\FFMpegDriver; | ||||||
|  | use FFMpeg\FFProbe; | ||||||
|  | use FFMpeg\Coordinate\TimeCode; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Video clip. | ||||||
|  |  * | ||||||
|  |  * Use input seeking, see http://trac.ffmpeg.org/wiki/Seeking | ||||||
|  |  */ | ||||||
|  | class Clip extends Video | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     /** @var TimeCode Start time */ | ||||||
|  |     private $start; | ||||||
|  | 
 | ||||||
|  |     /** @var TimeCode Duration */ | ||||||
|  |     private $duration; | ||||||
|  | 
 | ||||||
|  |     /** @var Video Parrent video */ | ||||||
|  |     private $video; | ||||||
|  | 
 | ||||||
|  |     public function __construct(Video $video, FFMpegDriver $driver, FFProbe $ffprobe, TimeCode $start, TimeCode $duration = null) | ||||||
|  |     { | ||||||
|  |         $this->start = $start; | ||||||
|  |         $this->duration = $duration; | ||||||
|  |         $this->video = $video; | ||||||
|  | 
 | ||||||
|  |         parent::__construct($video->getPathfile(), $driver, $ffprobe); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the video related to the frame. | ||||||
|  |      * | ||||||
|  |      * @return Video | ||||||
|  |      */ | ||||||
|  |     public function getVideo() | ||||||
|  |     { | ||||||
|  |         return $this->video; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Return base part of command. | ||||||
|  |      * | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     protected function basePartOfCommand() | ||||||
|  |     { | ||||||
|  |         $arr = array('-y', '-ss', (string) $this->start, '-i', $this->pathfile); | ||||||
|  | 
 | ||||||
|  |         if (is_null($this->duration) === false) { | ||||||
|  |             $arr[] = '-t'; | ||||||
|  |             $arr[] = (string) $this->duration; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $arr; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -72,8 +72,8 @@ class Concat extends AbstractMediaType | ||||||
|     /** |     /** | ||||||
|      * Saves the concatenated video in the given array, considering that the sources videos are all encoded with the same codec. |      * Saves the concatenated video in the given array, considering that the sources videos are all encoded with the same codec. | ||||||
|      * |      * | ||||||
|      * @param array   $outputPathfile |      * @param string  $outputPathfile | ||||||
|      * @param string  $streamCopy |      * @param bool    $streamCopy | ||||||
|      * |      * | ||||||
|      * @return Concat |      * @return Concat | ||||||
|      * |      * | ||||||
|  | @ -105,7 +105,7 @@ class Concat extends AbstractMediaType | ||||||
|                 if($count_videos != 0) |                 if($count_videos != 0) | ||||||
|                     $line .= "\n"; |                     $line .= "\n"; | ||||||
| 
 | 
 | ||||||
|                 $line .= "file ".$videoPath; |                 $line .= "file " . addcslashes($videoPath, '\'"\\\0 '); | ||||||
| 
 | 
 | ||||||
|                 fwrite($fileStream, $line); |                 fwrite($fileStream, $line); | ||||||
| 
 | 
 | ||||||
|  | @ -144,6 +144,7 @@ class Concat extends AbstractMediaType | ||||||
|             $this->driver->command($commands); |             $this->driver->command($commands); | ||||||
|         } catch (ExecutionFailureException $e) { |         } catch (ExecutionFailureException $e) { | ||||||
|             $this->cleanupTemporaryFile($outputPathfile); |             $this->cleanupTemporaryFile($outputPathfile); | ||||||
|  |             // TODO@v1: paste this line into an `finally` block.
 | ||||||
|             $this->cleanupTemporaryFile($sourcesFile); |             $this->cleanupTemporaryFile($sourcesFile); | ||||||
|             throw new RuntimeException('Unable to save concatenated video', $e->getCode(), $e); |             throw new RuntimeException('Unable to save concatenated video', $e->getCode(), $e); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -115,7 +115,9 @@ class Frame extends AbstractMediaType | ||||||
|             $commands = array_merge($commands, $filter->apply($this)); |             $commands = array_merge($commands, $filter->apply($this)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (!$returnBase64) { | ||||||
|             $commands = array_merge($commands, array($pathfile)); |             $commands = array_merge($commands, array($pathfile)); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             if(!$returnBase64) { |             if(!$returnBase64) { | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| <?php | <?php | ||||||
| 
 |  | ||||||
| /* | /* | ||||||
|  * This file is part of PHP-FFmpeg. |  * This file is part of PHP-FFmpeg. | ||||||
|  * |  * | ||||||
|  | @ -8,228 +7,13 @@ | ||||||
|  * For the full copyright and license information, please view the LICENSE |  * For the full copyright and license information, please view the LICENSE | ||||||
|  * file that was distributed with this source code. |  * file that was distributed with this source code. | ||||||
|  */ |  */ | ||||||
| 
 |  | ||||||
| namespace FFMpeg\Media; | namespace FFMpeg\Media; | ||||||
| 
 | 
 | ||||||
| use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |  | ||||||
| use FFMpeg\Coordinate\TimeCode; | use FFMpeg\Coordinate\TimeCode; | ||||||
| use FFMpeg\Coordinate\Dimension; | use FFMpeg\Coordinate\Dimension; | ||||||
| use FFMpeg\Filters\Audio\SimpleFilter; |  | ||||||
| use FFMpeg\Exception\InvalidArgumentException; |  | ||||||
| use FFMpeg\Exception\RuntimeException; |  | ||||||
| use FFMpeg\Filters\Video\VideoFilters; |  | ||||||
| use FFMpeg\Filters\FilterInterface; |  | ||||||
| use FFMpeg\Format\FormatInterface; |  | ||||||
| use FFMpeg\Format\ProgressableInterface; |  | ||||||
| use FFMpeg\Format\AudioInterface; |  | ||||||
| use FFMpeg\Format\VideoInterface; |  | ||||||
| use Neutron\TemporaryFilesystem\Manager as FsManager; |  | ||||||
| 
 | 
 | ||||||
| class Video extends Audio | class Video extends AbstractVideo | ||||||
| { | { | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      * |  | ||||||
|      * @return VideoFilters |  | ||||||
|      */ |  | ||||||
|     public function filters() |  | ||||||
|     { |  | ||||||
|         return new VideoFilters($this); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * {@inheritdoc} |  | ||||||
|      * |  | ||||||
|      * @return Video |  | ||||||
|      */ |  | ||||||
|     public function addFilter(FilterInterface $filter) |  | ||||||
|     { |  | ||||||
|         $this->filters->add($filter); |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Exports the video in the desired format, applies registered filters. |  | ||||||
|      * |  | ||||||
|      * @param FormatInterface $format |  | ||||||
|      * @param string          $outputPathfile |  | ||||||
|      * |  | ||||||
|      * @return Video |  | ||||||
|      * |  | ||||||
|      * @throws RuntimeException |  | ||||||
|      */ |  | ||||||
|     public function save(FormatInterface $format, $outputPathfile) |  | ||||||
|     { |  | ||||||
|         $commands = array('-y', '-i', $this->pathfile); |  | ||||||
| 
 |  | ||||||
|         $filters = clone $this->filters; |  | ||||||
|         $filters->add(new SimpleFilter($format->getExtraParams(), 10)); |  | ||||||
| 
 |  | ||||||
|         if ($this->driver->getConfiguration()->has('ffmpeg.threads')) { |  | ||||||
|             $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads')))); |  | ||||||
|         } |  | ||||||
|         if ($format instanceof VideoInterface) { |  | ||||||
|             if (null !== $format->getVideoCodec()) { |  | ||||||
|                 $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec()))); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if ($format instanceof AudioInterface) { |  | ||||||
|             if (null !== $format->getAudioCodec()) { |  | ||||||
|                 $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec()))); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         foreach ($filters as $filter) { |  | ||||||
|             $commands = array_merge($commands, $filter->apply($this, $format)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($format instanceof VideoInterface) { |  | ||||||
|             $commands[] = '-b:v'; |  | ||||||
|             $commands[] = $format->getKiloBitrate() . 'k'; |  | ||||||
|             $commands[] = '-refs'; |  | ||||||
|             $commands[] = '6'; |  | ||||||
|             $commands[] = '-coder'; |  | ||||||
|             $commands[] = '1'; |  | ||||||
|             $commands[] = '-sc_threshold'; |  | ||||||
|             $commands[] = '40'; |  | ||||||
|             $commands[] = '-flags'; |  | ||||||
|             $commands[] = '+loop'; |  | ||||||
|             $commands[] = '-me_range'; |  | ||||||
|             $commands[] = '16'; |  | ||||||
|             $commands[] = '-subq'; |  | ||||||
|             $commands[] = '7'; |  | ||||||
|             $commands[] = '-i_qfactor'; |  | ||||||
|             $commands[] = '0.71'; |  | ||||||
|             $commands[] = '-qcomp'; |  | ||||||
|             $commands[] = '0.6'; |  | ||||||
|             $commands[] = '-qdiff'; |  | ||||||
|             $commands[] = '4'; |  | ||||||
|             $commands[] = '-trellis'; |  | ||||||
|             $commands[] = '1'; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($format instanceof AudioInterface) { |  | ||||||
|             if (null !== $format->getAudioKiloBitrate()) { |  | ||||||
|                 $commands[] = '-b:a'; |  | ||||||
|                 $commands[] = $format->getAudioKiloBitrate() . 'k'; |  | ||||||
|             } |  | ||||||
|             if (null !== $format->getAudioChannels()) { |  | ||||||
|                 $commands[] = '-ac'; |  | ||||||
|                 $commands[] = $format->getAudioChannels(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // If the user passed some additional parameters
 |  | ||||||
|         if ($format instanceof VideoInterface) { |  | ||||||
|             if (null !== $format->getAdditionalParameters()) { |  | ||||||
|                 foreach ($format->getAdditionalParameters() as $additionalParameter) { |  | ||||||
|                     $commands[] = $additionalParameter; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Merge Filters into one command
 |  | ||||||
|         $videoFilterVars = $videoFilterProcesses = []; |  | ||||||
|         for($i=0;$i<count($commands);$i++) { |  | ||||||
|             $command = $commands[$i]; |  | ||||||
|             if ( $command == '-vf' ) { |  | ||||||
|                 $commandSplits = explode(";", $commands[$i + 1]); |  | ||||||
|                 if ( count($commandSplits) == 1 ) { |  | ||||||
|                     $commandSplit = $commandSplits[0]; |  | ||||||
|                     $command = trim($commandSplit); |  | ||||||
|                     if ( preg_match("/^\[in\](.*?)\[out\]$/is", $command, $match) ) { |  | ||||||
|                         $videoFilterProcesses[] = $match[1]; |  | ||||||
|                     } else { |  | ||||||
|                         $videoFilterProcesses[] = $command;    |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     foreach($commandSplits as $commandSplit) { |  | ||||||
|                         $command = trim($commandSplit); |  | ||||||
|                         if ( preg_match("/^\[[^\]]+\](.*?)\[[^\]]+\]$/is", $command, $match) ) { |  | ||||||
|                             $videoFilterProcesses[] = $match[1]; |  | ||||||
|                         } else { |  | ||||||
|                             $videoFilterVars[] = $command; |  | ||||||
|                         }                 |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 unset($commands[$i]); |  | ||||||
|                 unset($commands[$i + 1]); |  | ||||||
|                 $i++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         $videoFilterCommands = $videoFilterVars; |  | ||||||
|         $lastInput = 'in'; |  | ||||||
|         foreach($videoFilterProcesses as $i => $process) { |  | ||||||
|             $command = '[' . $lastInput .']'; |  | ||||||
|             $command .= $process; |  | ||||||
|             $lastInput = 'p' . $i; |  | ||||||
|             if ( $i == count($videoFilterProcesses) - 1 ) { |  | ||||||
|                 $command .= '[out]'; |  | ||||||
|             } else { |  | ||||||
|                 $command .= '[' . $lastInput . ']'; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             $videoFilterCommands[] = $command; |  | ||||||
|         } |  | ||||||
|         $videoFilterCommand = implode(";", $videoFilterCommands); |  | ||||||
|          |  | ||||||
|         if ( $videoFilterCommand ) { |  | ||||||
|             $commands[] = '-vf'; |  | ||||||
|             $commands[] = $videoFilterCommand; |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         $fs = FsManager::create(); |  | ||||||
|         $fsId = uniqid('ffmpeg-passes'); |  | ||||||
|         $passPrefix = $fs->createTemporaryDirectory(0777, 50, $fsId) . '/' . uniqid('pass-'); |  | ||||||
|         $passes = array(); |  | ||||||
|         $totalPasses = $format->getPasses(); |  | ||||||
| 
 |  | ||||||
|         if (1 > $totalPasses) { |  | ||||||
|             throw new InvalidArgumentException('Pass number should be a positive value.'); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for ($i = 1; $i <= $totalPasses; $i++) { |  | ||||||
|             $pass = $commands; |  | ||||||
| 
 |  | ||||||
|             if ($totalPasses > 1) { |  | ||||||
|                 $pass[] = '-pass'; |  | ||||||
|                 $pass[] = $i; |  | ||||||
|                 $pass[] = '-passlogfile'; |  | ||||||
|                 $pass[] = $passPrefix; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             $pass[] = $outputPathfile; |  | ||||||
| 
 |  | ||||||
|             $passes[] = $pass; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $failure = null; |  | ||||||
| 
 |  | ||||||
|         foreach ($passes as $pass => $passCommands) { |  | ||||||
|             try { |  | ||||||
|                 /** add listeners here */ |  | ||||||
|                 $listeners = null; |  | ||||||
| 
 |  | ||||||
|                 if ($format instanceof ProgressableInterface) { |  | ||||||
|                     $listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 $this->driver->command($passCommands, false, $listeners); |  | ||||||
|             } catch (ExecutionFailureException $e) { |  | ||||||
|                 $failure = $e; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $fs->clean($fsId); |  | ||||||
| 
 |  | ||||||
|         if (null !== $failure) { |  | ||||||
|             throw new RuntimeException('Encoding failed', $failure->getCode(), $failure); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Gets the frame at timecode. |      * Gets the frame at timecode. | ||||||
|  | @ -265,4 +49,16 @@ class Video extends Audio | ||||||
|     { |     { | ||||||
|         return new Concat($sources, $this->driver, $this->ffprobe); |         return new Concat($sources, $this->driver, $this->ffprobe); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Clips the video at the given time(s). | ||||||
|  |      * | ||||||
|  |      * @param TimeCode $start Start time | ||||||
|  |      * @param TimeCode $duration Duration | ||||||
|  |      * @return \FFMpeg\Media\Clip | ||||||
|  |      */ | ||||||
|  |     public function clip(TimeCode $start, TimeCode $duration = null) | ||||||
|  |     { | ||||||
|  |         return new Clip($this, $this->driver, $this->ffprobe, $start, $duration); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| namespace FFMpeg\Media; | namespace FFMpeg\Media; | ||||||
| 
 | 
 | ||||||
| use Alchemy\BinaryDriver\Exception\ExecutionFailureException; | use Alchemy\BinaryDriver\Exception\ExecutionFailureException; | ||||||
|  | use FFMpeg\Exception\InvalidArgumentException; | ||||||
| use FFMpeg\Filters\Waveform\WaveformFilterInterface; | use FFMpeg\Filters\Waveform\WaveformFilterInterface; | ||||||
| use FFMpeg\Filters\Waveform\WaveformFilters; | use FFMpeg\Filters\Waveform\WaveformFilters; | ||||||
| use FFMpeg\Driver\FFMpegDriver; | use FFMpeg\Driver\FFMpegDriver; | ||||||
|  | @ -20,17 +21,26 @@ use FFMpeg\Exception\RuntimeException; | ||||||
| 
 | 
 | ||||||
| class Waveform extends AbstractMediaType | class Waveform extends AbstractMediaType | ||||||
| { | { | ||||||
|     /** @var Video */ |     const DEFAULT_COLOR = '#000000'; | ||||||
|     private $audio; |  | ||||||
|     private $width; |  | ||||||
|     private $height; |  | ||||||
| 
 | 
 | ||||||
|     public function __construct(Audio $audio, FFMpegDriver $driver, FFProbe $ffprobe, $width, $height) |     /** @var Video */ | ||||||
|  |     protected $audio; | ||||||
|  |     protected $width; | ||||||
|  |     protected $height; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @var array | ||||||
|  |      */ | ||||||
|  |     protected $colors; | ||||||
|  | 
 | ||||||
|  |     public function __construct(Audio $audio, FFMpegDriver $driver, FFProbe $ffprobe, $width, $height, $colors = array(self::DEFAULT_COLOR)) | ||||||
|     { |     { | ||||||
|         parent::__construct($audio->getPathfile(), $driver, $ffprobe); |         parent::__construct($audio->getPathfile(), $driver, $ffprobe); | ||||||
|         $this->audio = $audio; |         $this->audio = $audio; | ||||||
|         $this->width = $width; |         $this->width = $width; | ||||||
|         $this->height = $height; |         $this->height = $height; | ||||||
|  | 
 | ||||||
|  |         $this->setColors($colors); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -65,6 +75,55 @@ class Waveform extends AbstractMediaType | ||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Parameter should be an array containing at least one valid color represented as a HTML color string. For | ||||||
|  |      * example #FFFFFF or #000000. By default the color is set to black. Keep in mind that if you save the waveform
 | ||||||
|  |      * as jpg file, it will appear completely black and to avoid this you can set the waveform color to white (#FFFFFF).
 | ||||||
|  |      * Saving waveforms to png is strongly suggested. | ||||||
|  |      * | ||||||
|  |      * @param array $colors | ||||||
|  |      */ | ||||||
|  |     public function setColors(array $colors) | ||||||
|  |     { | ||||||
|  |         foreach ($colors as $row => $value) | ||||||
|  |         { | ||||||
|  |             if (!preg_match('/^#(?:[0-9a-fA-F]{6})$/', $value)) | ||||||
|  |             { | ||||||
|  |                 //invalid color
 | ||||||
|  |                 //unset($colors[$row]);
 | ||||||
|  | 
 | ||||||
|  |                 throw new InvalidArgumentException("The provided color '$value' is invalid"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (count($colors)) | ||||||
|  |         { | ||||||
|  |             $this->colors = $colors; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns an array of colors that will be passed to ffmpeg to use for waveform generation. Colors are applied ONLY | ||||||
|  |      * to the waveform. Background cannot be controlled that easily and it is probably easier to save the waveform | ||||||
|  |      * as a transparent png file and then add background of choice. | ||||||
|  |      * | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     public function getColors() | ||||||
|  |     { | ||||||
|  |         return $this->colors; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Compiles the selected colors into a string, using a pipe separator. | ||||||
|  |      * | ||||||
|  |      * @return string | ||||||
|  |      */ | ||||||
|  |     protected function compileColors() | ||||||
|  |     { | ||||||
|  |         return implode('|', $this->colors); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Saves the waveform in the given filename. |      * Saves the waveform in the given filename. | ||||||
|      * |      * | ||||||
|  | @ -81,8 +140,8 @@ class Waveform extends AbstractMediaType | ||||||
|          * @see http://ffmpeg.org/ffmpeg.html#Main-options
 |          * @see http://ffmpeg.org/ffmpeg.html#Main-options
 | ||||||
|          */ |          */ | ||||||
|         $commands = array( |         $commands = array( | ||||||
|             '-i', $this->pathfile, '-filter_complex', |             '-y', '-i', $this->pathfile, '-filter_complex', | ||||||
|             'showwavespic=s='.$this->width.'x'.$this->height, |             'showwavespic=colors='.$this->compileColors().':s='.$this->width.'x'.$this->height, | ||||||
|             '-frames:v', '1' |             '-frames:v', '1' | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								tests/Functional/AudioConcatenationTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								tests/Functional/AudioConcatenationTest.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Tests\FFMpeg\Functional; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Format\Audio\Mp3; | ||||||
|  | use FFMpeg\Media\Audio; | ||||||
|  | 
 | ||||||
|  | class AudioConcatenationTest extends FunctionalTestCase | ||||||
|  | { | ||||||
|  |     public function testSimpleAudioFileConcatTest() | ||||||
|  |     { | ||||||
|  |         $ffmpeg = $this->getFFMpeg(); | ||||||
|  |          | ||||||
|  |         $files = [ | ||||||
|  |             __DIR__ . '/../files/Jahzzar_-_05_-_Siesta.mp3', | ||||||
|  |             __DIR__ . '/../files/02_-_Favorite_Secrets.mp3', | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $audio = $ffmpeg->open(reset($files)); | ||||||
|  | 
 | ||||||
|  |         $this->assertInstanceOf('FFMpeg\Media\Audio', $audio); | ||||||
|  |          | ||||||
|  |         clearstatcache(); | ||||||
|  |         $filename = __DIR__ . '/output/concat-output.mp3'; | ||||||
|  | 
 | ||||||
|  |         $audio->concat($files)->saveFromSameCodecs($filename, TRUE); | ||||||
|  |          | ||||||
|  |         $this->assertFileExists($filename); | ||||||
|  |         unlink($filename); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -12,10 +12,23 @@ class FFProbeTest extends FunctionalTestCase | ||||||
|         $this->assertGreaterThan(0, count($ffprobe->streams(__DIR__ . '/../files/Audio.mp3'))); |         $this->assertGreaterThan(0, count($ffprobe->streams(__DIR__ . '/../files/Audio.mp3'))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function testValidateExistingFile() | ||||||
|  |     { | ||||||
|  |         $ffprobe = FFProbe::create(); | ||||||
|  |         $this->assertTrue($ffprobe->isValid(__DIR__ . '/../files/sample.3gp')); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public function testValidateNonExistingFile() | ||||||
|  |     { | ||||||
|  |         $ffprobe = FFProbe::create(); | ||||||
|  |         $this->assertFalse($ffprobe->isValid(__DIR__ . '/../files/WrongFile.mp4')); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @expectedException FFMpeg\Exception\RuntimeException |      * @expectedException FFMpeg\Exception\RuntimeException | ||||||
|      */ |      */ | ||||||
|     public function testProbeOnUnexistantFile() |     public function testProbeOnNonExistantFile() | ||||||
|     { |     { | ||||||
|         $ffprobe = FFProbe::create(); |         $ffprobe = FFProbe::create(); | ||||||
|         $ffprobe->streams('/path/to/no/file'); |         $ffprobe->streams('/path/to/no/file'); | ||||||
|  |  | ||||||
|  | @ -3,8 +3,9 @@ | ||||||
| namespace Tests\FFMpeg\Functional; | namespace Tests\FFMpeg\Functional; | ||||||
| 
 | 
 | ||||||
| use FFMpeg\FFMpeg; | use FFMpeg\FFMpeg; | ||||||
|  | use PHPUnit\Framework\TestCase; | ||||||
| 
 | 
 | ||||||
| abstract class FunctionalTestCase extends \PHPUnit_Framework_TestCase | abstract class FunctionalTestCase extends TestCase | ||||||
| { | { | ||||||
|     /** |     /** | ||||||
|      * @return FFMpeg |      * @return FFMpeg | ||||||
|  |  | ||||||
|  | @ -13,4 +13,11 @@ class PointTest extends TestCase | ||||||
|         $this->assertEquals(4, $point->getX()); |         $this->assertEquals(4, $point->getX()); | ||||||
|         $this->assertEquals(25, $point->getY()); |         $this->assertEquals(25, $point->getY()); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public function testDynamicPointGetters() | ||||||
|  |     { | ||||||
|  |         $point = new Point("t*100", "t", true); | ||||||
|  |         $this->assertEquals("t*100", $point->getX()); | ||||||
|  |         $this->assertEquals("t", $point->getY()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4,8 +4,9 @@ namespace Tests\FFMpeg\Unit; | ||||||
| 
 | 
 | ||||||
| use FFMpeg\FFMpegServiceProvider; | use FFMpeg\FFMpegServiceProvider; | ||||||
| use Silex\Application; | use Silex\Application; | ||||||
|  | use PHPUnit\Framework\TestCase as BaseTestCase; | ||||||
| 
 | 
 | ||||||
| class FFMpegServiceProviderTest extends \PHPUnit_Framework_TestCase | class FFMpegServiceProviderTest extends BaseTestCase | ||||||
| { | { | ||||||
|     public function testWithConfig() |     public function testWithConfig() | ||||||
|     { |     { | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								tests/Unit/Filters/Audio/CustomFilterTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tests/Unit/Filters/Audio/CustomFilterTest.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Tests\FFMpeg\Unit\Filters\Audio; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Filters\Audio\CustomFilter; | ||||||
|  | use FFMpeg\Filters\Audio\FrameRateFilter; | ||||||
|  | use Tests\FFMpeg\Unit\TestCase; | ||||||
|  | use FFMpeg\Coordinate\FrameRate; | ||||||
|  | 
 | ||||||
|  | class CustomFilterTest extends TestCase | ||||||
|  | { | ||||||
|  |     public function testApplyCustomFilter() | ||||||
|  |     { | ||||||
|  |         $audio = $this->getAudioMock(); | ||||||
|  |         $format = $this->getMock('FFMpeg\Format\AudioInterface'); | ||||||
|  | 
 | ||||||
|  |         $filter = new CustomFilter('whatever i put would end up as a filter'); | ||||||
|  |         $this->assertEquals(array('-af', 'whatever i put would end up as a filter'), $filter->apply($audio, $format)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								tests/Unit/Filters/Frame/CustomFrameFilterTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/Unit/Filters/Frame/CustomFrameFilterTest.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Tests\FFMpeg\Unit\Filters\Frame; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Filters\Frame\CustomFrameFilter; | ||||||
|  | use Tests\FFMpeg\Unit\TestCase; | ||||||
|  | 
 | ||||||
|  | class CustomFrameFilterTest extends TestCase | ||||||
|  | { | ||||||
|  |     public function testApplyCustomFrameFilter() | ||||||
|  |     { | ||||||
|  |         $frame = $this->getFrameMock(); | ||||||
|  | 
 | ||||||
|  |         $filter = new CustomFrameFilter('whatever i put would end up as a filter'); | ||||||
|  |         $this->assertEquals(array('-vf', 'whatever i put would end up as a filter'), $filter->apply($frame)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -12,7 +12,7 @@ class ExtractMultipleFramesFilterTest extends TestCase | ||||||
|     /** |     /** | ||||||
|      * @dataProvider provideFrameRates |      * @dataProvider provideFrameRates | ||||||
|      */ |      */ | ||||||
|     public function testApply($frameRate, $destinationFolder, $duration, $modulus, $expected) |     public function testApply($frameRate, $frameFileType,$destinationFolder, $duration, $modulus, $expected) | ||||||
|     { |     { | ||||||
|         $video = $this->getVideoMock(); |         $video = $this->getVideoMock(); | ||||||
|         $pathfile = '/path/to/file'.mt_rand(); |         $pathfile = '/path/to/file'.mt_rand(); | ||||||
|  | @ -34,18 +34,41 @@ class ExtractMultipleFramesFilterTest extends TestCase | ||||||
|             ->will($this->returnValue($streams)); |             ->will($this->returnValue($streams)); | ||||||
| 
 | 
 | ||||||
|         $filter = new ExtractMultipleFramesFilter($frameRate, $destinationFolder); |         $filter = new ExtractMultipleFramesFilter($frameRate, $destinationFolder); | ||||||
|  |         $filter->setFrameFileType($frameFileType); | ||||||
|         $this->assertEquals($expected, $filter->apply($video, $format)); |         $this->assertEquals($expected, $filter->apply($video, $format)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function provideFrameRates() |     public function provideFrameRates() | ||||||
|     { |     { | ||||||
|         return array( |         return array( | ||||||
|             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, '/', 100, 2, array('-vf', 'fps=1/1', '/frame-%03d.jpg')), |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'jpg', '/', 100, 2, array('-vf', 'fps=1/1', '/frame-%03d.jpg')), | ||||||
|             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, '/', 100, 2, array('-vf', 'fps=1/2', '/frame-%02d.jpg')), |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'jpg', '/', 100, 2, array('-vf', 'fps=1/2', '/frame-%02d.jpg')), | ||||||
|             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, '/', 100, 2, array('-vf', 'fps=1/5', '/frame-%02d.jpg')), |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'jpg', '/', 100, 2, array('-vf', 'fps=1/5', '/frame-%02d.jpg')), | ||||||
|             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, '/', 100, 2, array('-vf', 'fps=1/10', '/frame-%02d.jpg')), |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'jpg', '/', 100, 2, array('-vf', 'fps=1/10', '/frame-%02d.jpg')), | ||||||
|             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, '/', 100, 2, array('-vf', 'fps=1/30', '/frame-%02d.jpg')), |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'jpg', '/', 100, 2, array('-vf', 'fps=1/30', '/frame-%02d.jpg')), | ||||||
|             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, '/', 100, 2, array('-vf', 'fps=1/60', '/frame-%02d.jpg')), |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'jpg', '/', 100, 2, array('-vf', 'fps=1/60', '/frame-%02d.jpg')), | ||||||
|  | 
 | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'jpeg', '/', 100, 2, array('-vf', 'fps=1/1', '/frame-%03d.jpeg')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'jpeg', '/', 100, 2, array('-vf', 'fps=1/2', '/frame-%02d.jpeg')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'jpeg', '/', 100, 2, array('-vf', 'fps=1/5', '/frame-%02d.jpeg')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'jpeg', '/', 100, 2, array('-vf', 'fps=1/10', '/frame-%02d.jpeg')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'jpeg', '/', 100, 2, array('-vf', 'fps=1/30', '/frame-%02d.jpeg')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'jpeg', '/', 100, 2, array('-vf', 'fps=1/60', '/frame-%02d.jpeg')), | ||||||
|  | 
 | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'png', '/', 100, 2, array('-vf', 'fps=1/1', '/frame-%03d.png')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'png', '/', 100, 2, array('-vf', 'fps=1/2', '/frame-%02d.png')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'png', '/', 100, 2, array('-vf', 'fps=1/5', '/frame-%02d.png')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'png', '/', 100, 2, array('-vf', 'fps=1/10', '/frame-%02d.png')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'png', '/', 100, 2, array('-vf', 'fps=1/30', '/frame-%02d.png')), | ||||||
|  |             array(ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'png', '/', 100, 2, array('-vf', 'fps=1/60', '/frame-%02d.png')), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @expectedException \FFMpeg\Exception\InvalidArgumentException | ||||||
|  |      */ | ||||||
|  |     public function testInvalidFrameFileType() { | ||||||
|  |         $filter = new ExtractMultipleFramesFilter('1/1', '/'); | ||||||
|  |         $filter->setFrameFileType('webm'); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ class VideoProgressListenerTest extends TestCase | ||||||
|     /** |     /** | ||||||
|      * @dataProvider provideData |      * @dataProvider provideData | ||||||
|      */ |      */ | ||||||
|     public function testHandle($size, $duration, |     public function testHandle($size, $duration, $newVideoDuration, | ||||||
|         $data, $expectedPercent, $expectedRemaining, $expectedRate, |         $data, $expectedPercent, $expectedRemaining, $expectedRate, | ||||||
|         $data2, $expectedPercent2, $expectedRemaining2, $expectedRate2, |         $data2, $expectedPercent2, $expectedRemaining2, $expectedRate2, | ||||||
|         $currentPass, $totalPass |         $currentPass, $totalPass | ||||||
|  | @ -26,7 +26,7 @@ class VideoProgressListenerTest extends TestCase | ||||||
|                 'duration' => $duration, |                 'duration' => $duration, | ||||||
|             )))); |             )))); | ||||||
| 
 | 
 | ||||||
|         $listener = new VideoProgressListener($ffprobe, __FILE__, $currentPass, $totalPass); |         $listener = new VideoProgressListener($ffprobe, __FILE__, $currentPass, $totalPass, $newVideoDuration); | ||||||
|         $phpunit = $this; |         $phpunit = $this; | ||||||
|         $n = 0; |         $n = 0; | ||||||
|         $listener->on('progress', function ($percent, $remaining, $rate) use (&$n, $phpunit, $expectedPercent, $expectedRemaining, $expectedRate, $expectedPercent2, $expectedRemaining2, $expectedRate2) { |         $listener->on('progress', function ($percent, $remaining, $rate) use (&$n, $phpunit, $expectedPercent, $expectedRemaining, $expectedRate, $expectedPercent2, $expectedRemaining2, $expectedRate2) { | ||||||
|  | @ -57,6 +57,7 @@ class VideoProgressListenerTest extends TestCase | ||||||
|             array( |             array( | ||||||
|                 147073958, |                 147073958, | ||||||
|                 281.147533, |                 281.147533, | ||||||
|  |                 281.147533, | ||||||
|                 'frame=  206 fps=202 q=10.0 size=     571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0', |                 'frame=  206 fps=202 q=10.0 size=     571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0', | ||||||
|                 2, |                 2, | ||||||
|                 0, |                 0, | ||||||
|  | @ -71,6 +72,7 @@ class VideoProgressListenerTest extends TestCase | ||||||
|             array( |             array( | ||||||
|                 147073958, |                 147073958, | ||||||
|                 281.147533, |                 281.147533, | ||||||
|  |                 281.147533, | ||||||
|                 'frame=  206 fps=202 q=10.0 size=     571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0', |                 'frame=  206 fps=202 q=10.0 size=     571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0', | ||||||
|                 1, |                 1, | ||||||
|                 0, |                 0, | ||||||
|  | @ -81,6 +83,21 @@ class VideoProgressListenerTest extends TestCase | ||||||
|                 3868, |                 3868, | ||||||
|                 1, |                 1, | ||||||
|                 2 |                 2 | ||||||
|  |             ), | ||||||
|  |             array( | ||||||
|  |                 147073958, | ||||||
|  |                 281.147533, | ||||||
|  |                 35, | ||||||
|  |                 'frame=  206 fps=202 q=10.0 size=     571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0', | ||||||
|  |                 60, | ||||||
|  |                 0, | ||||||
|  |                 0, | ||||||
|  |                 'frame=  854 fps=113 q=20.0 size=    4430kB time=00:00:33.04 bitrate=1098.5kbits/s dup=36 drop=0', | ||||||
|  |                 97, | ||||||
|  |                 0, | ||||||
|  |                 3868, | ||||||
|  |                 2, | ||||||
|  |                 2 | ||||||
|             ) |             ) | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
							
								
								
									
										67
									
								
								tests/Unit/Media/ClipTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								tests/Unit/Media/ClipTest.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | <?php | ||||||
|  | namespace Tests\FFMpeg\Unit\Media; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Media\Clip; | ||||||
|  | 
 | ||||||
|  | class ClipTest extends AbstractMediaTestCase | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @dataProvider provideBuildOptions | ||||||
|  |      */ | ||||||
|  |     public function testBuildCommand($startValue, $durationValue, $commands) | ||||||
|  |     { | ||||||
|  |         $configuration = $this->getConfigurationMock(); | ||||||
|  | 
 | ||||||
|  |         $driver = $this->getFFMpegDriverMock(); | ||||||
|  |         $driver->expects($this->any()) | ||||||
|  |             ->method('getConfiguration') | ||||||
|  |             ->will($this->returnValue($configuration)); | ||||||
|  | 
 | ||||||
|  |         $ffprobe = $this->getFFProbeMock(); | ||||||
|  | 
 | ||||||
|  |         $start = $this->getTimeCodeMock(); | ||||||
|  |         $start->expects($this->once()) | ||||||
|  |             ->method('__toString') | ||||||
|  |             ->will($this->returnValue($startValue)); | ||||||
|  | 
 | ||||||
|  |         $duration = null; | ||||||
|  |         if (null !== $durationValue) { | ||||||
|  |             $duration = $this->getTimeCodeMock(); | ||||||
|  |             $duration->expects($this->once()) | ||||||
|  |                 ->method('__toString') | ||||||
|  |                 ->will($this->returnValue($durationValue)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $outputPathfile = '/target/file'; | ||||||
|  | 
 | ||||||
|  |         $format = $this->getMock('FFMpeg\Format\VideoInterface'); | ||||||
|  |         $format->expects($this->any()) | ||||||
|  |             ->method('getPasses') | ||||||
|  |             ->will($this->returnValue(1)); | ||||||
|  |         $format->expects($this->any()) | ||||||
|  |             ->method('getExtraParams') | ||||||
|  |             ->will($this->returnValue(array())); | ||||||
|  | 
 | ||||||
|  |         $clip = new Clip($this->getVideoMock(__FILE__), $driver, $ffprobe, $start, $duration); | ||||||
|  |         $fc = $clip->getFinalCommand($format, $outputPathfile); | ||||||
|  | 
 | ||||||
|  |         $this->assertCount(1, $fc); | ||||||
|  |         $this->assertStringStartsWith(implode(' ', $commands), $fc[0]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function provideBuildOptions() | ||||||
|  |     { | ||||||
|  |         return array( | ||||||
|  |             array('SS01', null, array( | ||||||
|  |                     '-y', '-ss', 'SS01', | ||||||
|  |                     '-i', __FILE__) | ||||||
|  |             ), | ||||||
|  |             array('SS02', 'D02', array( | ||||||
|  |                     '-y', '-ss', 'SS02', | ||||||
|  |                     '-i', __FILE__, | ||||||
|  |                     '-t', 'D02') | ||||||
|  |             ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -61,7 +61,9 @@ class FrameTest extends AbstractMediaTestCase | ||||||
| 
 | 
 | ||||||
|         $pathfile = '/target/destination'; |         $pathfile = '/target/destination'; | ||||||
| 
 | 
 | ||||||
|  |         if (!$base64) { | ||||||
|             array_push($commands, $pathfile); |             array_push($commands, $pathfile); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         $driver->expects($this->once()) |         $driver->expects($this->once()) | ||||||
|             ->method('command') |             ->method('command') | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ class WaveformTest extends AbstractMediaTestCase | ||||||
|             ->method('command') |             ->method('command') | ||||||
|             ->with($commands); |             ->with($commands); | ||||||
| 
 | 
 | ||||||
|         $waveform = new Waveform($this->getAudioMock(__FILE__), $driver, $ffprobe, 640, 120); |         $waveform = new Waveform($this->getAudioMock(__FILE__), $driver, $ffprobe, 640, 120, ['#FFFFFF']); | ||||||
|         $this->assertSame($waveform, $waveform->save($pathfile)); |         $this->assertSame($waveform, $waveform->save($pathfile)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -61,8 +61,8 @@ class WaveformTest extends AbstractMediaTestCase | ||||||
|         return array( |         return array( | ||||||
|             array( |             array( | ||||||
|                 array( |                 array( | ||||||
|                     '-i', NULL, '-filter_complex', |                     '-y', '-i', NULL, '-filter_complex', | ||||||
|                     'showwavespic=s=640x120', |                     'showwavespic=colors=#FFFFFF:s=640x120', | ||||||
|                     '-frames:v', '1', |                     '-frames:v', '1', | ||||||
|                 ), |                 ), | ||||||
|             ), |             ), | ||||||
|  |  | ||||||
|  | @ -2,7 +2,9 @@ | ||||||
| 
 | 
 | ||||||
| namespace Tests\FFMpeg\Unit; | namespace Tests\FFMpeg\Unit; | ||||||
| 
 | 
 | ||||||
| class TestCase extends \PHPUnit_Framework_TestCase | use PHPUnit\Framework\TestCase as BaseTestCase; | ||||||
|  | 
 | ||||||
|  | class TestCase extends BaseTestCase | ||||||
| { | { | ||||||
|     public function assertScalar($value) |     public function assertScalar($value) | ||||||
|     { |     { | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								tests/files/02_-_Favorite_Secrets.mp3
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/files/02_-_Favorite_Secrets.mp3
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								tests/files/Jahzzar_-_05_-_Siesta.mp3
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/files/Jahzzar_-_05_-_Siesta.mp3
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue