Merge pull request #79 from alchemy-fr/fix-78
Fix using rotate and resize filters at the same time (#78)
This commit is contained in:
commit
f47cf9f008
11 changed files with 165 additions and 8 deletions
|
|
@ -1,7 +1,11 @@
|
|||
CHANGELOG
|
||||
---------
|
||||
|
||||
* 0.4.2 (xx-xx-xx)
|
||||
* 0.4.3 (11-xx-2013)
|
||||
|
||||
* Fix using rotate and resize filters at the same time (#78)
|
||||
|
||||
* 0.4.2 (11-29-2013)
|
||||
|
||||
* Add Rotate filter.
|
||||
* Remove time_start metadata when using synchronize filter
|
||||
|
|
|
|||
|
|
@ -10,12 +10,20 @@ Check another amazing repo : [PHP FFMpeg extras](https://github.com/alchemy-fr/P
|
|||
|
||||
## Your attention please
|
||||
|
||||
### How this library works :
|
||||
|
||||
This library requires a working FFMpeg install. You will need both FFMpeg and FFProbe binaries to use it.
|
||||
Be sure that these binaries can be located with system PATH to get the benefit of the binary detection,
|
||||
otherwise you should have to explicitely give the binaries path on load.
|
||||
|
||||
For Windows users : Please find the binaries at http://ffmpeg.zeranoe.com/builds/.
|
||||
|
||||
### Known issues :
|
||||
|
||||
- Using rotate and resize will produce a corrupted output when using
|
||||
[libav](http://libav.org/) 0.8. The bug is fixed in version 9. This bug does not
|
||||
appear in latest ffmpeg version.
|
||||
|
||||
## Installation
|
||||
|
||||
The recommended way to install PHP-FFMpeg is through [Composer](https://getcomposer.org).
|
||||
|
|
|
|||
|
|
@ -226,8 +226,13 @@ class FFProbe
|
|||
$parseIsToDo = false;
|
||||
|
||||
if ($allowJson && $this->optionsTester->has('-print_format')) {
|
||||
// allowed in latest PHP-FFmpeg version
|
||||
$commands[] = '-print_format';
|
||||
$commands[] = 'json';
|
||||
} elseif ($allowJson && $this->optionsTester->has('-of')) {
|
||||
// option has changed in avconv 9
|
||||
$commands[] = '-of';
|
||||
$commands[] = 'json';
|
||||
} else {
|
||||
$parseIsToDo = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,21 @@ abstract class AbstractData implements \Countable
|
|||
return $this->properties[$property];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property value given its name.
|
||||
*
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return AbstractData
|
||||
*/
|
||||
public function set($property, $value)
|
||||
{
|
||||
$this->properties[$property] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all property names.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -55,6 +55,16 @@ class RotateFilter implements VideoFilterInterface
|
|||
*/
|
||||
public function apply(Video $video, VideoInterface $format)
|
||||
{
|
||||
if (in_array($this->angle, array(self::ROTATE_90, self::ROTATE_270), true)) {
|
||||
foreach ($video->getStreams()->videos() as $stream) {
|
||||
if ($stream->has('width') && $stream->has('height')) {
|
||||
$width = $stream->get('width');
|
||||
$stream->set('width', $stream->get('height'));
|
||||
$stream->set('height', $width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array('-vf', $this->angle, '-metadata:s:v:0', 'rotate=0');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,9 +96,9 @@ class VideoFilters extends AudioFilters
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function rotate($angle, $priority = 0)
|
||||
public function rotate($angle)
|
||||
{
|
||||
$this->media->addFilter(new RotateFilter($angle, $priority));
|
||||
$this->media->addFilter(new RotateFilter($angle, 30));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,18 @@ use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
|||
|
||||
abstract class AbstractStreamableMedia extends AbstractMediaType
|
||||
{
|
||||
private $streams;
|
||||
|
||||
/**
|
||||
* @return StreamCollection
|
||||
*/
|
||||
public function getStreams()
|
||||
{
|
||||
return $this->ffprobe->streams($this->pathfile);
|
||||
if (null === $this->streams) {
|
||||
$this->streams = $this->ffprobe->streams($this->pathfile);
|
||||
}
|
||||
|
||||
return $this->streams;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ use FFMpeg\FFMpeg;
|
|||
|
||||
abstract class FunctionalTestCase extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @return FFMpeg
|
||||
*/
|
||||
public function getFFMpeg()
|
||||
{
|
||||
return FFMpeg::create(array('timeout' => 300));
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\Filters\Video\ResizeFilter;
|
||||
use FFMpeg\Filters\Video\RotateFilter;
|
||||
use FFMpeg\Format\Video\X264;
|
||||
use FFMpeg\Media\Video;
|
||||
|
||||
|
|
@ -54,4 +57,62 @@ class VideoTranscodeTest extends FunctionalTestCase
|
|||
$this->setExpectedException('FFMpeg\Exception\RuntimeException');
|
||||
$video->save(new X264('libvo_aacenc'), __DIR__ . '/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
public function testTranscodePortraitVideo()
|
||||
{
|
||||
$info = $this->getNameAndVersion();
|
||||
|
||||
if ($info['name'] === 'avconv' && version_compare($info['version'], '0.9', '<')) {
|
||||
$this->markTestSkipped('This version of avconv is buggy and does not support this test.');
|
||||
}
|
||||
|
||||
$filename = __DIR__ . '/output/output-x264.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__ . '/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__ . '/../../files/portrait.MOV');
|
||||
|
||||
$video->filters()
|
||||
->resize(new Dimension(320, 240), ResizeFilter::RESIZEMODE_INSET)
|
||||
->rotate(RotateFilter::ROTATE_90);
|
||||
$video->save(new X264('libvo_aacenc'), $filename);
|
||||
|
||||
$dimension = $ffmpeg->getFFProbe()
|
||||
->streams($filename)
|
||||
->videos()
|
||||
->first()
|
||||
->getDimensions();
|
||||
|
||||
$this->assertLessThan(1, $dimension->getRatio(false)->getValue());
|
||||
$this->assertEquals(240, $dimension->getHeight());
|
||||
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
private function getNameAndVersion()
|
||||
{
|
||||
$binary = $this
|
||||
->getFFMpeg()
|
||||
->getFFMpegDriver()
|
||||
->getProcessBuilderFactory()
|
||||
->getBinary();
|
||||
|
||||
$output = $matches = null;
|
||||
exec($binary . ' -version 2>&1', $output);
|
||||
|
||||
if (!isset($output[0])) {
|
||||
return array('name' => null, 'version' => null);
|
||||
}
|
||||
|
||||
preg_match('/^([a-z]+)\s+version\s+([0-9\.]+)/i', $output[0], $matches);
|
||||
|
||||
if (count($matches) > 0) {
|
||||
return array('name' => $matches[1], 'version' => $matches[2]);
|
||||
}
|
||||
|
||||
return array('name' => null, 'version' => null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,63 @@
|
|||
|
||||
namespace FFMpeg\Tests\Filters\Video;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\RotateFilter;
|
||||
use FFMpeg\Tests\TestCase;
|
||||
|
||||
class RotateFilterTest extends TestCase
|
||||
{
|
||||
public function testApply()
|
||||
/**
|
||||
* @dataProvider provide90degresTranspositions
|
||||
*/
|
||||
public function testApplyWithSizeTransformation($value)
|
||||
{
|
||||
$stream = new Stream(array('width' => 320, 'height' => 240, 'codec_type' => 'video'));
|
||||
$streams = new StreamCollection(array($stream));
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$video->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$format = $this->getMock('FFMpeg\Format\VideoInterface');
|
||||
|
||||
$filter = new RotateFilter(RotateFilter::ROTATE_90);
|
||||
$this->assertEquals(array('-vf', RotateFilter::ROTATE_90, '-metadata:s:v:0', 'rotate=0'), $filter->apply($video, $format));
|
||||
$filter = new RotateFilter($value);
|
||||
$this->assertEquals(array('-vf', $value, '-metadata:s:v:0', 'rotate=0'), $filter->apply($video, $format));
|
||||
|
||||
$this->assertEquals(240, $stream->get('width'));
|
||||
$this->assertEquals(320, $stream->get('height'));
|
||||
}
|
||||
|
||||
public function provide90degresTranspositions()
|
||||
{
|
||||
return array(
|
||||
array(RotateFilter::ROTATE_90),
|
||||
array(RotateFilter::ROTATE_270),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDegresWithoutTranspositions
|
||||
*/
|
||||
public function testApplyWithoutSizeTransformation($value)
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$video->expects($this->never())
|
||||
->method('getStreams');
|
||||
|
||||
$format = $this->getMock('FFMpeg\Format\VideoInterface');
|
||||
|
||||
$filter = new RotateFilter($value);
|
||||
$this->assertEquals(array('-vf', $value, '-metadata:s:v:0', 'rotate=0'), $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
public function provideDegresWithoutTranspositions()
|
||||
{
|
||||
return array(
|
||||
array(RotateFilter::ROTATE_180),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
BIN
tests/files/portrait.MOV
Normal file
BIN
tests/files/portrait.MOV
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue