Merge branch '0.4'

This commit is contained in:
Romain Neutron 2013-11-29 11:38:54 +01:00
commit 4db04f70e4
16 changed files with 176 additions and 54 deletions

View file

@ -1,6 +1,12 @@
CHANGELOG
---------
* 0.4.2 (xx-xx-xx)
* Add Rotate filter.
* Remove time_start metadata when using synchronize filter
* Remove restriction on filesystem resources.
* 0.4.1 (11-26-2013)
* Add Clip filter (@guimeira)

View file

@ -11,7 +11,7 @@ Check another amazing repo : [PHP FFMpeg extras](https://github.com/alchemy-fr/P
## Your attention please
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,
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/.
@ -75,8 +75,17 @@ $ffmpeg = FFMpeg\FFMpeg::create(array(
### Manipulate media
`FFMpeg\FFMpeg` creates media based on file paths. To open a file path, use the
`FFMpeg\FFMpeg::open` method.
`FFMpeg\FFMpeg` creates media based on URIs. URIs could be either a pointer to a
local filesystem resource, an HTTP resource or any resource supported by FFmpeg.
**Note** : To list all supported resource type of your FFmpeg build, use the
`-protocols` command :
```
ffmpeg -protocols
```
To open a resource, use the `FFMpeg\FFMpeg::open` method.
```php
$ffmpeg->open('video.mpeg');
@ -145,6 +154,20 @@ $video
->synchronize();
```
###### Rotate
Rotates a video to a given angle.
```php
$video->filters()->rotate($angle);
```
The `$angle` parameter must be one of the following constants :
- `FFMpeg\Filters\Video\RotateFilter::ROTATE_90` : 90° clockwise
- `FFMpeg\Filters\Video\RotateFilter::ROTATE_180` : 180°
- `FFMpeg\Filters\Video\RotateFilter::ROTATE_270` : 90° counterclockwise
###### Resize
Resizes a video to a given size.

View file

@ -14,6 +14,7 @@ namespace FFMpeg;
use Alchemy\BinaryDriver\ConfigurationInterface;
use FFMpeg\Driver\FFMpegDriver;
use FFMpeg\Exception\InvalidArgumentException;
use FFMpeg\Exception\RuntimeException;
use FFMpeg\Media\Audio;
use FFMpeg\Media\Video;
use Psr\Log\LoggerInterface;
@ -88,12 +89,10 @@ class FFMpeg
*/
public function open($pathfile)
{
if (!file_exists($pathfile)) {
throw new InvalidArgumentException(sprintf('File %s does not exists', $pathfile));
if (null === $streams = $this->ffprobe->streams($pathfile)) {
throw new RuntimeException(sprintf('Unable to probe "%s".', $pathfile));
}
$streams = $this->ffprobe->streams($pathfile);
if (0 < count($streams->videos())) {
return new Video($pathfile, $this->driver, $this->ffprobe);
} elseif (0 < count($streams->audios())) {

View file

@ -0,0 +1,73 @@
<?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\Video;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\Exception\InvalidArgumentException;
use FFMpeg\Exception\RuntimeException;
use FFMpeg\Media\Video;
use FFMpeg\Format\VideoInterface;
class RotateFilter implements VideoFilterInterface
{
const ROTATE_90 = 'transpose=1';
const ROTATE_180 = 'hflip,vflip';
const ROTATE_270 = 'transpose=2';
/** @var string */
private $angle;
/** @var integer */
private $priority;
public function __construct($angle, $priority = 0)
{
$this->setAngle($angle);
$this->priority = (int) $priority;
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return $this->priority;
}
/**
* @return Dimension
*/
public function getAngle()
{
return $this->angle;
}
/**
* {@inheritdoc}
*/
public function apply(Video $video, VideoInterface $format)
{
return array('-vf', $this->angle, '-metadata:s:v:0', 'rotate=0');
}
private function setAngle($angle)
{
switch ($angle) {
case self::ROTATE_90:
case self::ROTATE_180:
case self::ROTATE_270:
$this->angle = $angle;
break;
default:
throw new InvalidArgumentException('Invalid angle value.');
}
}
}

View file

@ -39,6 +39,6 @@ class SynchronizeFilter implements VideoFilterInterface
*/
public function apply(Video $video, VideoInterface $format)
{
return array('-async', '1');
return array('-async', '1', '-metadata:s:v:0', 'start_time=0');
}
}

View file

@ -95,4 +95,11 @@ class VideoFilters extends AudioFilters
return $this;
}
public function rotate($angle, $priority = 0)
{
$this->media->addFilter(new RotateFilter($angle, $priority));
return $this;
}
}

View file

@ -154,6 +154,10 @@ abstract class AbstractProgressListener extends EventEmitter implements Listener
$this->initialize();
}
if (null === $this->totalSize || null === $this->duration) {
return;
}
$matches = array();
if (preg_match($this->getPattern(), $progress, $matches) !== 1) {
@ -226,9 +230,14 @@ abstract class AbstractProgressListener extends EventEmitter implements Listener
private function initialize()
{
$format = $this->ffprobe->format($this->pathfile);
try {
$format = $this->ffprobe->format($this->pathfile);
} catch (RuntimeException $e) {
return;
}
if (false === $format->has('size') || false === $format->has('duration')) {
return;
throw new RuntimeException(sprintf('Unable to probe format for %s', $this->pathfile));
}

View file

@ -30,8 +30,6 @@ abstract class AbstractMediaType implements MediaTypeInterface
public function __construct($pathfile, FFMpegDriver $driver, FFProbe $ffprobe)
{
$this->ensureFileIsPresent($pathfile);
$this->pathfile = $pathfile;
$this->driver = $driver;
$this->ffprobe = $ffprobe;
@ -106,15 +104,6 @@ abstract class AbstractMediaType implements MediaTypeInterface
return $this->filters;
}
protected function ensureFileIsPresent($filename)
{
if (!is_file($filename) || !is_readable($filename)) {
throw new InvalidArgumentException(sprintf(
'%s is not present or not readable', $filename
));
}
}
protected function cleanupTemporaryFile($filename)
{
if (file_exists($filename) && is_writable($filename)) {

View file

@ -3,6 +3,7 @@
namespace FFMpeg\Functional;
use FFMpeg\Format\Video\X264;
use FFMpeg\Media\Video;
class VideoTranscodeTest extends FunctionalTestCase
{
@ -35,4 +36,22 @@ class VideoTranscodeTest extends FunctionalTestCase
$this->assertFileExists($filename);
unlink($filename);
}
/**
* @expectedException \FFMpeg\Exception\RuntimeException
*/
public function testTranscodeInvalidFile()
{
$ffmpeg = $this->getFFMpeg();
$ffmpeg->open(__DIR__ . '/../../files/UnknownFileTest.ogv');
}
public function testSaveInvalidForgedVideo()
{
$ffmpeg = $this->getFFMpeg();
$video = new Video(__DIR__ . '/../../files/UnknownFileTest.ogv', $ffmpeg->getFFMpegDriver(), $ffmpeg->getFFProbe());
$this->setExpectedException('FFMpeg\Exception\RuntimeException');
$video->save(new X264('libvo_aacenc'), __DIR__ . '/output/output-x264.mp4');
}
}

View file

@ -10,7 +10,8 @@ use FFMpeg\FFProbe\DataMapping\Stream;
class FFMpegTest Extends TestCase
{
/**
* @expectedException FFMpeg\Exception\InvalidArgumentException
* @expectedException \FFMpeg\Exception\RuntimeException
* @expectedExceptionMessage Unable to probe "/path/to/unknown/file".
*/
public function testOpenInvalid()
{
@ -58,7 +59,7 @@ class FFMpegTest Extends TestCase
}
/**
* @expectedException FFMpeg\Exception\InvalidArgumentException
* @expectedException \FFMpeg\Exception\InvalidArgumentException
*/
public function testOpenUnknown()
{

View file

@ -0,0 +1,27 @@
<?php
namespace FFMpeg\Tests\Filters\Video;
use FFMpeg\Filters\Video\RotateFilter;
use FFMpeg\Tests\TestCase;
class RotateFilterTest extends TestCase
{
public function testApply()
{
$video = $this->getVideoMock();
$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));
}
/**
* @expectedException \FFMpeg\Exception\InvalidArgumentException
* @expectedExceptionMessage Invalid angle value.
*/
public function testApplyInvalidAngle()
{
new RotateFilter('90');
}
}

View file

@ -13,6 +13,6 @@ class SynchronizeFilterTest extends TestCase
$format = $this->getMock('FFMpeg\Format\VideoInterface');
$filter = new SynchronizeFilter();
$this->assertEquals(array('-async', '1'), $filter->apply($video, $format));
$this->assertEquals(array('-async', '1', '-metadata:s:v:0', 'start_time=0'), $filter->apply($video, $format));
}
}

View file

@ -6,5 +6,4 @@ use FFMpeg\Tests\TestCase;
abstract class AbstractMediaTestCase extends TestCase
{
abstract public function testWithInvalidFile();
}

View file

@ -9,17 +9,6 @@ use FFMpeg\Format\AudioInterface;
class AudioTest extends AbstractStreamableTestCase
{
/**
* @expectedException FFMpeg\Exception\InvalidArgumentException
*/
public function testWithInvalidFile()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
new Audio('/no/file', $driver, $ffprobe);
}
public function testFiltersReturnsAudioFilters()
{
$driver = $this->getFFMpegDriverMock();

View file

@ -6,14 +6,6 @@ use FFMpeg\Media\Frame;
class FrameTest extends AbstractMediaTestCase
{
/**
* @expectedException FFMpeg\Exception\InvalidArgumentException
*/
public function testWithInvalidFile()
{
new Frame($this->getVideoMock('/No/file'), $this->getFFMpegDriverMock(), $this->getFFProbeMock(), $this->getTimeCodeMock());
}
public function testGetTimeCode()
{
$driver = $this->getFFMpegDriverMock();

View file

@ -9,17 +9,6 @@ use FFMpeg\Format\VideoInterface;
class VideoTest extends AbstractStreamableTestCase
{
/**
* @expectedException FFMpeg\Exception\InvalidArgumentException
*/
public function testWithInvalidFile()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
new Video('/no/file', $driver, $ffprobe);
}
public function testFiltersReturnsVideoFilters()
{
$driver = $this->getFFMpegDriverMock();