Merge pull request #57 from alchemy-fr/frame-display-ratio-fixer
Add frame display ratio fixer filter
This commit is contained in:
		
				commit
				
					
						5529f2e6e9
					
				
			
		
					 12 changed files with 181 additions and 16 deletions
				
			
		|  | @ -4,6 +4,7 @@ CHANGELOG | ||||||
| * 0.3.3 (xx-xx-2013) | * 0.3.3 (xx-xx-2013) | ||||||
| 
 | 
 | ||||||
|   * Add convenient Stream::getDimensions method to extract video dimension. |   * Add convenient Stream::getDimensions method to extract video dimension. | ||||||
|  |   * Add DisplayRatioFixer Frame filter. | ||||||
| 
 | 
 | ||||||
| * 0.3.2 (08-08-2013) | * 0.3.2 (08-08-2013) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -47,9 +47,13 @@ class FiltersCollection implements \Countable, \IteratorAggregate | ||||||
|     public function getIterator() |     public function getIterator() | ||||||
|     { |     { | ||||||
|         if (null === $this->sorted) { |         if (null === $this->sorted) { | ||||||
|  |             if (0 === count($this->filters)) { | ||||||
|  |                 $this->sorted = $this->filters; | ||||||
|  |             } else { | ||||||
|                 krsort($this->filters); |                 krsort($this->filters); | ||||||
|                 $this->sorted = call_user_func_array('array_merge', $this->filters); |                 $this->sorted = call_user_func_array('array_merge', $this->filters); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return new \ArrayIterator($this->sorted); |         return new \ArrayIterator($this->sorted); | ||||||
|     } |     } | ||||||
|  |  | ||||||
							
								
								
									
										58
									
								
								src/FFMpeg/Filters/Frame/DisplayRatioFixerFilter.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/FFMpeg/Filters/Frame/DisplayRatioFixerFilter.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * This file is part of PHP-FFmpeg. | ||||||
|  |  * | ||||||
|  |  * (c) Alchemy <dev.team@alchemy.fr> | ||||||
|  |  * | ||||||
|  |  * For the full copyright and license information, please view the LICENSE | ||||||
|  |  * file that was distributed with this source code. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace FFMpeg\Filters\Frame; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Exception\RuntimeException; | ||||||
|  | use FFMpeg\Media\Frame; | ||||||
|  | 
 | ||||||
|  | class DisplayRatioFixerFilter implements FrameFilterInterface | ||||||
|  | { | ||||||
|  |     /** @var integer */ | ||||||
|  |     private $priority; | ||||||
|  | 
 | ||||||
|  |     public function __construct($priority = 0) | ||||||
|  |     { | ||||||
|  |         $this->priority = $priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public function getPriority() | ||||||
|  |     { | ||||||
|  |         return $this->priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritdoc} | ||||||
|  |      */ | ||||||
|  |     public function apply(Frame $frame) | ||||||
|  |     { | ||||||
|  |         $dimensions = null; | ||||||
|  |         $commands = array(); | ||||||
|  | 
 | ||||||
|  |         foreach ($frame->getVideo()->getStreams() as $stream) { | ||||||
|  |             if ($stream->isVideo()) { | ||||||
|  |                 try { | ||||||
|  |                     $dimensions = $stream->getDimensions(); | ||||||
|  |                     $commands[] = '-s'; | ||||||
|  |                     $commands[] = $dimensions->getWidth() . 'x' . $dimensions->getHeight(); | ||||||
|  |                     break; | ||||||
|  |                 } catch (RuntimeException $e) { | ||||||
|  | 
 | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $commands; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -13,9 +13,8 @@ namespace FFMpeg\Filters\Frame; | ||||||
| 
 | 
 | ||||||
| use FFMpeg\Filters\FilterInterface; | use FFMpeg\Filters\FilterInterface; | ||||||
| use FFMpeg\Media\Frame; | use FFMpeg\Media\Frame; | ||||||
| use FFMpeg\Format\FrameInterface; |  | ||||||
| 
 | 
 | ||||||
| interface FrameFilterInterface extends FilterInterface | interface FrameFilterInterface extends FilterInterface | ||||||
| { | { | ||||||
|     public function apply(Frame $frame, FrameInterface $format); |     public function apply(Frame $frame); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,4 +21,19 @@ class FrameFilters | ||||||
|     { |     { | ||||||
|         $this->frame = $frame; |         $this->frame = $frame; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fixes the display ratio of the output frame. | ||||||
|  |      *  | ||||||
|  |      * In case the sample ratio and display ratio are different, image may be | ||||||
|  |      * anamorphozed. This filter fixes this by specifying the output size. | ||||||
|  |      *  | ||||||
|  |      * @return FrameFilters | ||||||
|  |      */ | ||||||
|  |     public function fixDisplayRatio() | ||||||
|  |     { | ||||||
|  |         $this->frame->addFilter(new DisplayRatioFixerFilter()); | ||||||
|  | 
 | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,16 +18,30 @@ use FFMpeg\Driver\FFMpegDriver; | ||||||
| use FFMpeg\FFProbe; | use FFMpeg\FFProbe; | ||||||
| use FFMpeg\Exception\RuntimeException; | use FFMpeg\Exception\RuntimeException; | ||||||
| use FFMpeg\Coordinate\TimeCode; | use FFMpeg\Coordinate\TimeCode; | ||||||
|  | use FFMpeg\Media\Video; | ||||||
| 
 | 
 | ||||||
| class Frame extends AbstractMediaType | class Frame extends AbstractMediaType | ||||||
| { | { | ||||||
|     /** @var TimeCode */ |     /** @var TimeCode */ | ||||||
|     private $timecode; |     private $timecode; | ||||||
|  |     /** @var Video */ | ||||||
|  |     private $video; | ||||||
| 
 | 
 | ||||||
|     public function __construct($pathfile, FFMpegDriver $driver, FFProbe $ffprobe, TimeCode $timecode) |     public function __construct(Video $video, FFMpegDriver $driver, FFProbe $ffprobe, TimeCode $timecode) | ||||||
|     { |     { | ||||||
|         parent::__construct($pathfile, $driver, $ffprobe); |         parent::__construct($video->getPathfile(), $driver, $ffprobe); | ||||||
|         $this->timecode = $timecode; |         $this->timecode = $timecode; | ||||||
|  |         $this->video = $video; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the video related to the frame. | ||||||
|  |      * | ||||||
|  |      * @return Video | ||||||
|  |      */ | ||||||
|  |     public function getVideo() | ||||||
|  |     { | ||||||
|  |         return $this->video; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -83,16 +97,22 @@ class Frame extends AbstractMediaType | ||||||
|                 '-y', '-ss', (string) $this->timecode, |                 '-y', '-ss', (string) $this->timecode, | ||||||
|                 '-i', $this->pathfile, |                 '-i', $this->pathfile, | ||||||
|                 '-vframes', '1', |                 '-vframes', '1', | ||||||
|                 '-f', 'image2', $pathfile |                 '-f', 'image2' | ||||||
|             ); |             ); | ||||||
|         } else { |         } else { | ||||||
|             $commands = array( |             $commands = array( | ||||||
|                 '-y', '-i', $this->pathfile, |                 '-y', '-i', $this->pathfile, | ||||||
|                 '-vframes', '1', '-ss', (string) $this->timecode, |                 '-vframes', '1', '-ss', (string) $this->timecode, | ||||||
|                 '-f', 'image2', $pathfile |                 '-f', 'image2' | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         foreach ($this->filters as $filter) { | ||||||
|  |             $commands = array_merge($commands, $filter->apply($this)); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         $commands = array_merge($commands, array($pathfile)); | ||||||
|  | 
 | ||||||
|         try { |         try { | ||||||
|             $this->driver->command($commands); |             $this->driver->command($commands); | ||||||
|         } catch (ExecutionFailureException $e) { |         } catch (ExecutionFailureException $e) { | ||||||
|  |  | ||||||
|  | @ -159,6 +159,6 @@ class Video extends Audio | ||||||
|      */ |      */ | ||||||
|     public function frame(TimeCode $at) |     public function frame(TimeCode $at) | ||||||
|     { |     { | ||||||
|         return new Frame($this->pathfile, $this->driver, $this->ffprobe, $at); |         return new Frame($this, $this->driver, $this->ffprobe, $at); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -30,6 +30,12 @@ class FiltersCollectionTest extends TestCase | ||||||
|         $this->assertCount(2, $coll->getIterator()); |         $this->assertCount(2, $coll->getIterator()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function testEmptyIterator() | ||||||
|  |     { | ||||||
|  |         $coll = new FiltersCollection(); | ||||||
|  |         $this->assertInstanceOf('\ArrayIterator', $coll->getIterator()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function testIteratorSort() |     public function testIteratorSort() | ||||||
|     { |     { | ||||||
|         $coll = new FiltersCollection(); |         $coll = new FiltersCollection(); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace FFMpeg\Tests\Filters\Frame; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Tests\TestCase; | ||||||
|  | use FFMpeg\Filters\Frame\DisplayRatioFixerFilter; | ||||||
|  | use FFMpeg\Media\Frame; | ||||||
|  | use FFMpeg\Coordinate\TimeCode; | ||||||
|  | use FFMpeg\FFProbe\DataMapping\StreamCollection; | ||||||
|  | use FFMpeg\FFProbe\DataMapping\Stream; | ||||||
|  | 
 | ||||||
|  | class DisplayRatioFixerFilterTest extends TestCase | ||||||
|  | { | ||||||
|  |     public function testApply() | ||||||
|  |     { | ||||||
|  |         $stream = new Stream(array('codec_type' => 'video', 'width' => 960, 'height' => 720)); | ||||||
|  |         $streams = new StreamCollection(array($stream)); | ||||||
|  |          | ||||||
|  |         $video = $this->getVideoMock(__FILE__); | ||||||
|  |         $video->expects($this->once()) | ||||||
|  |                 ->method('getStreams') | ||||||
|  |                 ->will($this->returnValue($streams)); | ||||||
|  |          | ||||||
|  |         $frame = new Frame($video, $this->getFFMpegDriverMock(), $this->getFFProbeMock(), new TimeCode(0, 0, 0, 0)); | ||||||
|  |         $filter = new DisplayRatioFixerFilter(); | ||||||
|  |         $this->assertEquals(array('-s', '960x720'), $filter->apply($frame)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								tests/FFMpeg/Tests/Filters/Frame/FrameFiltersTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/FFMpeg/Tests/Filters/Frame/FrameFiltersTest.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace FFMpeg\Tests\Filters\Frame; | ||||||
|  | 
 | ||||||
|  | use FFMpeg\Tests\TestCase; | ||||||
|  | use FFMpeg\Filters\Frame\FrameFilters; | ||||||
|  | 
 | ||||||
|  | class FrameFiltersTest extends TestCase | ||||||
|  | { | ||||||
|  |     public function testResize() | ||||||
|  |     { | ||||||
|  |         $frame = $this->getFrameMock(); | ||||||
|  |         $filters = new FrameFilters($frame); | ||||||
|  | 
 | ||||||
|  |         $frame->expects($this->once()) | ||||||
|  |             ->method('addFilter') | ||||||
|  |             ->with($this->isInstanceOf('FFMpeg\Filters\Frame\DisplayRatioFixerFilter')); | ||||||
|  | 
 | ||||||
|  |         $filters->fixDisplayRatio(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -11,7 +11,7 @@ class FrameTest extends AbstractMediaTestCase | ||||||
|      */ |      */ | ||||||
|     public function testWithInvalidFile() |     public function testWithInvalidFile() | ||||||
|     { |     { | ||||||
|         new Frame('/No/file', $this->getFFMpegDriverMock(), $this->getFFProbeMock(), $this->getTimeCodeMock()); |         new Frame($this->getVideoMock('/No/file'), $this->getFFMpegDriverMock(), $this->getFFProbeMock(), $this->getTimeCodeMock()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testGetTimeCode() |     public function testGetTimeCode() | ||||||
|  | @ -20,7 +20,7 @@ class FrameTest extends AbstractMediaTestCase | ||||||
|         $ffprobe = $this->getFFProbeMock(); |         $ffprobe = $this->getFFProbeMock(); | ||||||
|         $timecode = $this->getTimeCodeMock(); |         $timecode = $this->getTimeCodeMock(); | ||||||
| 
 | 
 | ||||||
|         $frame = new Frame(__FILE__, $driver, $ffprobe, $timecode); |         $frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode); | ||||||
|         $this->assertSame($timecode, $frame->getTimeCode()); |         $this->assertSame($timecode, $frame->getTimeCode()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -30,7 +30,7 @@ class FrameTest extends AbstractMediaTestCase | ||||||
|         $ffprobe = $this->getFFProbeMock(); |         $ffprobe = $this->getFFProbeMock(); | ||||||
|         $timecode = $this->getTimeCodeMock(); |         $timecode = $this->getTimeCodeMock(); | ||||||
| 
 | 
 | ||||||
|         $frame = new Frame(__FILE__, $driver, $ffprobe, $timecode); |         $frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode); | ||||||
|         $this->assertInstanceOf('FFMpeg\Filters\Frame\FrameFilters', $frame->filters()); |         $this->assertInstanceOf('FFMpeg\Filters\Frame\FrameFilters', $frame->filters()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -50,7 +50,7 @@ class FrameTest extends AbstractMediaTestCase | ||||||
|             ->method('add') |             ->method('add') | ||||||
|             ->with($filter); |             ->with($filter); | ||||||
| 
 | 
 | ||||||
|         $frame = new Frame(__FILE__, $driver, $ffprobe, $timecode); |         $frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode); | ||||||
|         $frame->setFiltersCollection($filters); |         $frame->setFiltersCollection($filters); | ||||||
|         $frame->addFilter($filter); |         $frame->addFilter($filter); | ||||||
|     } |     } | ||||||
|  | @ -75,7 +75,7 @@ class FrameTest extends AbstractMediaTestCase | ||||||
|             ->method('command') |             ->method('command') | ||||||
|             ->with($commands); |             ->with($commands); | ||||||
| 
 | 
 | ||||||
|         $frame = new Frame(__FILE__, $driver, $ffprobe, $timecode); |         $frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode); | ||||||
|         $this->assertSame($frame, $frame->save($pathfile, $accurate)); |         $this->assertSame($frame, $frame->save($pathfile, $accurate)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,6 +40,13 @@ class TestCase extends \PHPUnit_Framework_TestCase | ||||||
|             ->getMock(); |             ->getMock(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function getFrameMock() | ||||||
|  |     { | ||||||
|  |         return $this->getMockBuilder('FFMpeg\Media\Frame') | ||||||
|  |             ->disableOriginalConstructor() | ||||||
|  |             ->getMock(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function getFFMpegDriverMock() |     public function getFFMpegDriverMock() | ||||||
|     { |     { | ||||||
|         return $this->getMockBuilder('FFMpeg\Driver\FFMpegDriver') |         return $this->getMockBuilder('FFMpeg\Driver\FFMpegDriver') | ||||||
|  | @ -122,10 +129,16 @@ class TestCase extends \PHPUnit_Framework_TestCase | ||||||
|             ->getMock(); |             ->getMock(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected function getVideoMock() |     protected function getVideoMock($filename = null) | ||||||
|     { |     { | ||||||
|         return $this->getMockBuilder('FFMpeg\Media\Video') |         $video = $this->getMockBuilder('FFMpeg\Media\Video') | ||||||
|             ->disableOriginalConstructor() |             ->disableOriginalConstructor() | ||||||
|             ->getMock(); |             ->getMock(); | ||||||
|  | 
 | ||||||
|  |         $video->expects($this->any()) | ||||||
|  |             ->method('getPathfile') | ||||||
|  |             ->will($this->returnValue($filename)); | ||||||
|  | 
 | ||||||
|  |         return $video; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue