[BC] Upgraded dependencies, dropped support for anything below PHP 8.0. (#849)

* GitHub actions + style fixes + updated packages

* Fixed workflows dir

* Support for PHP 8.1 (#1)

* Update README.md

* Revert some changes from upstream
This commit is contained in:
Pascal Baljet 2022-02-09 14:32:43 +01:00 committed by GitHub
commit 111c153428
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
335 changed files with 4394 additions and 28116 deletions

View file

@ -0,0 +1,83 @@
<?php
namespace Tests\FFMpeg\Unit\Coordinate;
use FFMpeg\Coordinate\AspectRatio;
use FFMpeg\Coordinate\Dimension;
use Tests\FFMpeg\Unit\TestCase;
class AspectRatioTest extends TestCase
{
/**
* @dataProvider provideDimensionsAndExpectedratio
*/
public function testFromDimensions($width, $height, $strategy, $expected, $calculatedWidth, $calculatedHeight, $modulus = 2)
{
$ratio = AspectRatio::create(new Dimension($width, $height), $strategy);
$this->assertEquals($expected, $ratio->getValue());
$this->assertEquals($calculatedHeight, $ratio->calculateHeight(240, $modulus));
$this->assertEquals($calculatedWidth, $ratio->calculateWidth(320, $modulus));
}
public function provideDimensionsAndExpectedratio()
{
return [
//AR_5_4
[720, 576, false, 5 / 4, 400, 192],
[720, 577, false, 5 / 4, 400, 192],
[720, 620, false, 720 / 620, 372, 206],
[720, 576, true, 5 / 4, 400, 192],
//AR_ROTATED_4_5
[576, 720, false, 4 / 5, 256, 300],
[576, 720, true, 4 / 5, 256, 300],
//AR_4_3
[320, 240, false, 4 / 3, 426, 180],
[320, 240, true, 4 / 3, 426, 180],
//AR_ROTATED_3_4
[240, 320, false, 3 / 4, 240, 320],
[240, 320, true, 3 / 4, 240, 320],
//AR_16_9
[1920, 1080, false, 16 / 9, 568, 136],
[1920, 1080, true, 16 / 9, 568, 136],
[1280, 720, false, 16 / 9, 568, 136],
[1280, 720, true, 16 / 9, 568, 136],
[3840, 2160, false, 16 / 9, 568, 136],
[3840, 2160, true, 16 / 9, 568, 136],
// modulus 4
[1920, 1080, false, 16 / 9, 568, 136, 4],
[1920, 1080, true, 16 / 9, 568, 136, 4],
[1280, 720, false, 16 / 9, 568, 136, 4],
[1280, 720, true, 16 / 9, 568, 136, 4],
[3840, 2160, false, 16 / 9, 568, 136, 4],
[3840, 2160, true, 16 / 9, 568, 136, 4],
// modulus 16
[1920, 1080, false, 16 / 9, 576, 128, 16],
[1920, 1080, true, 16 / 9, 576, 128, 16],
[1280, 720, false, 16 / 9, 576, 128, 16],
[1280, 720, true, 16 / 9, 576, 128, 16],
[3840, 2160, false, 16 / 9, 576, 128, 16],
[3840, 2160, true, 16 / 9, 576, 128, 16],
//AR_ROTATED_9_16
[1080, 1920, false, 9 / 16, 180, 426],
[1080, 1920, true, 9 / 16, 180, 426],
[720, 1280, false, 9 / 16, 180, 426],
[720, 1280, true, 9 / 16, 180, 426],
[2160, 3840, false, 9 / 16, 180, 426],
[2160, 3840, true, 9 / 16, 180, 426],
//AR_3_2
[360, 240, false, 3 / 2, 480, 160],
[360, 240, true, 3 / 2, 480, 160],
//AR_ROTATED_2_3
[240, 360, false, 2 / 3, 214, 360],
[240, 360, true, 2 / 3, 214, 360],
//AR_5_3
//AR_ROTATED_3_5
//AR_1_1
//AR_1_DOT_85_1
//AR_ROTATED_1_DOT_85
//AR_2_DOT_39_1
//AR_ROTATED_2_DOT_39
];
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Tests\FFMpeg\Unit\Coordinate;
use FFMpeg\Coordinate\Dimension;
use Tests\FFMpeg\Unit\TestCase;
class DimensionTest extends TestCase
{
/**
* @dataProvider provideInvalidDimensions
*/
public function testInvalidDimensions($width, $height)
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
new Dimension($width, $height);
}
public function provideInvalidDimensions()
{
return [
[320, 0],
[320, -10],
[0, 240],
[-10, 240],
[0, 0],
[0, -10],
[-10, 0],
];
}
public function testGetters()
{
$dimension = new Dimension(320, 240);
$this->assertEquals(320, $dimension->getWidth());
$this->assertEquals(240, $dimension->getHeight());
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Tests\FFMpeg\Unit\Coordinate;
use FFMpeg\Coordinate\FrameRate;
use Tests\FFMpeg\Unit\TestCase;
class FrameRateTest extends TestCase
{
public function testGetter()
{
$fr = new FrameRate(23.997);
$this->assertEquals(23.997, $fr->getValue());
}
/**
* @dataProvider provideInvalidFrameRates
*/
public function testInvalidFrameRate($value)
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
new FrameRate($value);
}
public function provideInvalidFrameRates()
{
return [
[0], [-1.5], [-2],
];
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Tests\FFMpeg\Unit\Coordinate;
use FFMpeg\Coordinate\Point;
use Tests\FFMpeg\Unit\TestCase;
class PointTest extends TestCase
{
public function testGetters()
{
$point = new Point(4, 25);
$this->assertEquals(4, $point->getX());
$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());
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Tests\FFMpeg\Unit\Coordinate;
use FFMpeg\Coordinate\TimeCode;
use Tests\FFMpeg\Unit\TestCase;
class TimeCodeTest extends TestCase
{
/**
* @dataProvider provideTimecodes
*/
public function testFromString($timecode, $expected)
{
$tc = TimeCode::fromString($timecode);
$this->assertEquals((string) $tc, $expected);
}
public function provideTimeCodes()
{
return [
['1:02:04:05:20', '26:04:05.20'],
['1:02:04:05.20', '26:04:05.20'],
['02:04:05:20', '02:04:05.20'],
['02:04:05.20', '02:04:05.20'],
['00:00:05.20', '00:00:05.20'],
['00:00:00.00', '00:00:00.00'],
];
}
public function testFromInvalidString()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
TimeCode::fromString('lalali lala');
}
/**
* @dataProvider provideSeconds
*/
public function testFromSeconds($seconds, $expected)
{
$tc = TimeCode::fromSeconds($seconds);
$this->assertEquals($expected, (string) $tc);
}
public function provideSeconds()
{
return [
[0.467, '00:00:00.47'],
[12.467, '00:00:12.47'],
[59.867, '00:00:59.87'],
[72.467, '00:01:12.47'],
[3599.467, '00:59:59.47'],
[3600.467, '01:00:00.47'],
[86422.467, '24:00:22.47'],
];
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Tests\FFMpeg\Unit\Driver;
use Alchemy\BinaryDriver\Configuration;
use FFMpeg\Driver\FFMpegDriver;
use Symfony\Component\Process\ExecutableFinder;
use Tests\FFMpeg\Unit\TestCase;
class FFMpegDriverTest extends TestCase
{
public function setUp(): void
{
$executableFinder = new ExecutableFinder();
$found = false;
foreach (['avconv', 'ffmpeg'] as $name) {
if (null !== $executableFinder->find($name)) {
$found = true;
break;
}
}
if (!$found) {
$this->markTestSkipped('Neither ffmpeg or avconv found');
}
}
public function testCreate()
{
$logger = $this->getLoggerMock();
$ffmpeg = FFMpegDriver::create($logger, []);
$this->assertInstanceOf('FFMpeg\Driver\FFMpegDriver', $ffmpeg);
$this->assertEquals($logger, $ffmpeg->getProcessRunner()->getLogger());
}
public function testCreateWithConfig()
{
$conf = new Configuration();
$ffmpeg = FFMpegDriver::create($this->getLoggerMock(), $conf);
$this->assertEquals($conf, $ffmpeg->getConfiguration());
}
public function testCreateFailureThrowsAnException()
{
$this->expectException('\FFMpeg\Exception\ExecutableNotFoundException');
FFMpegDriver::create($this->getLoggerMock(), ['ffmpeg.binaries' => '/path/to/nowhere']);
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace Tests\FFMpeg\Unit\Driver;
use Alchemy\BinaryDriver\Configuration;
use FFMpeg\Driver\FFProbeDriver;
use Symfony\Component\Process\ExecutableFinder;
use Tests\FFMpeg\Unit\TestCase;
class FFProbeDriverTest extends TestCase
{
public function setUp(): void
{
$executableFinder = new ExecutableFinder();
$found = false;
foreach (['avprobe', 'ffprobe'] as $name) {
if (null !== $executableFinder->find($name)) {
$found = true;
break;
}
}
if (!$found) {
$this->markTestSkipped('Neither ffprobe or avprobe found');
}
}
public function testCreate()
{
$logger = $this->getLoggerMock();
$ffprobe = FFProbeDriver::create([], $logger);
$this->assertInstanceOf('FFMpeg\Driver\FFProbeDriver', $ffprobe);
$this->assertEquals($logger, $ffprobe->getProcessRunner()->getLogger());
}
public function testCreateWithConfig()
{
$conf = new Configuration();
$ffprobe = FFProbeDriver::create($conf, $this->getLoggerMock());
$this->assertEquals($conf, $ffprobe->getConfiguration());
}
public function testCreateFailureThrowsAnException()
{
$this->expectException('\FFMpeg\Exception\ExecutableNotFoundException');
FFProbeDriver::create(['ffprobe.binaries' => '/path/to/nowhere']);
}
}

View file

@ -0,0 +1,110 @@
<?php
namespace Tests\FFMpeg\Unit;
use FFMpeg\FFMpeg;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
class FFMpegTest extends TestCase
{
public function testOpenInvalid()
{
$this->expectException(
'\FFMpeg\Exception\RuntimeException',
'Unable to probe "/path/to/unknown/file"'
);
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $this->getFFProbeMock());
$ffmpeg->open('/path/to/unknown/file');
}
public function testOpenAudio()
{
$streams = $this->getStreamCollectionMock();
$streams->expects($this->once())
->method('audios')
->will($this->returnValue(new StreamCollection([new Stream([])])));
$streams->expects($this->once())
->method('videos')
->will($this->returnValue([]));
$ffprobe = $this->getFFProbeMock();
$ffprobe->expects($this->once())
->method('streams')
->with(__FILE__)
->will($this->returnValue($streams));
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
$this->assertInstanceOf('FFMpeg\Media\Audio', $ffmpeg->open(__FILE__));
}
public function testOpenVideo()
{
$streams = $this->getStreamCollectionMock();
$streams->expects($this->once())
->method('videos')
->will($this->returnValue(new StreamCollection([new Stream([])])));
$streams->expects($this->never())
->method('audios');
$ffprobe = $this->getFFProbeMock();
$ffprobe->expects($this->once())
->method('streams')
->with(__FILE__)
->will($this->returnValue($streams));
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
$this->assertInstanceOf('FFMpeg\Media\Video', $ffmpeg->open(__FILE__));
}
public function testOpenUnknown()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$ffprobe = $this->getFFProbeMock();
$ffprobe->expects($this->once())
->method('streams')
->with(__FILE__)
->will($this->returnValue(new StreamCollection()));
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
$ffmpeg->open(__FILE__);
}
public function testCreateWithoutLoggerOrProbe()
{
$this->assertInstanceOf('FFMpeg\FFMpeg', FFMpeg::create());
}
public function testCreateWithLoggerAndProbe()
{
$logger = $this->getLoggerMock();
$ffprobe = $this->getFFProbeMock();
$ffmpeg = FFMpeg::create(['timeout' => 42], $logger, $ffprobe);
$this->assertInstanceOf('FFMpeg\FFMpeg', $ffmpeg);
$this->assertSame($logger, $ffmpeg->getFFMpegDriver()->getProcessRunner()->getLogger());
$this->assertSame($ffprobe, $ffmpeg->getFFProbe());
$this->assertSame(42, $ffmpeg->getFFMpegDriver()->getProcessBuilderFactory()->getTimeout());
}
public function testGetSetFFProbe()
{
$ffprobe = $this->getFFProbeMock();
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
$this->assertSame($ffprobe, $ffmpeg->getFFProbe());
$anotherFFProbe = $this->getFFProbeMock();
$ffmpeg->setFFProbe($anotherFFProbe);
$this->assertSame($anotherFFProbe, $ffmpeg->getFFProbe());
}
public function testGetSetDriver()
{
$driver = $this->getFFMpegDriverMock();
$ffmpeg = new FFMpeg($driver, $this->getFFProbeMock());
$this->assertSame($driver, $ffmpeg->getFFMpegDriver());
$anotherDriver = $this->getFFMpegDriverMock();
$ffmpeg->setFFMpegDriver($anotherDriver);
$this->assertSame($anotherDriver, $ffmpeg->getFFMpegDriver());
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Tests\FFMpeg\Unit\FFProbe\DataMapping;
use FFMpeg\FFProbe\DataMapping\AbstractData;
use Tests\FFMpeg\Unit\TestCase;
class AbstractDataTest extends TestCase
{
public function testHas()
{
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
$this->assertTrue($imp->has('key1'));
$this->assertTrue($imp->has('key2'));
$this->assertFalse($imp->has('value1'));
$this->assertFalse($imp->has('key3'));
}
public function testGet()
{
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals('value1', $imp->get('key1'));
$this->assertEquals('value2', $imp->get('key2'));
}
public function testGetDefault()
{
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
$this->assertSame('yololo', $imp->get('key3', 'yololo'));
}
public function testKeys()
{
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals(['key1', 'key2'], $imp->keys());
}
public function testAll()
{
$values = ['key1' => 'value1', 'key2' => 'value2'];
$imp = new Implementation($values);
$this->assertEquals($values, $imp->all());
}
}
class Implementation extends AbstractData
{
}

View file

@ -0,0 +1,89 @@
<?php
namespace Tests\FFMpeg\Unit\FFProbe\DataMapping;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use Tests\FFMpeg\Unit\TestCase;
class StreamCollectionTest extends TestCase
{
public function testAdd()
{
$stream = $this->getStreamMock();
$collection = new StreamCollection();
$this->assertEquals([], $collection->all());
$collection->add($stream);
$this->assertEquals([$stream], $collection->all());
$collection->add($stream);
$this->assertEquals([$stream, $stream], $collection->all());
}
public function testVideos()
{
$audio = $this->getStreamMock();
$audio->expects($this->once())
->method('isVideo')
->will($this->returnValue(false));
$video = $this->getStreamMock();
$video->expects($this->once())
->method('isVideo')
->will($this->returnValue(true));
$collection = new StreamCollection([$audio, $video]);
$videos = $collection->videos();
$this->assertInstanceOf('FFMpeg\FFProbe\DataMapping\StreamCollection', $videos);
$this->assertCount(1, $videos);
$this->assertEquals([$video], $videos->all());
}
public function testAudios()
{
$audio = $this->getStreamMock();
$audio->expects($this->once())
->method('isAudio')
->will($this->returnValue(true));
$video = $this->getStreamMock();
$video->expects($this->once())
->method('isAudio')
->will($this->returnValue(false));
$collection = new StreamCollection([$audio, $video]);
$audios = $collection->audios();
$this->assertInstanceOf('FFMpeg\FFProbe\DataMapping\StreamCollection', $audios);
$this->assertCount(1, $audios);
$this->assertEquals([$audio], $audios->all());
}
public function testCount()
{
$stream = $this->getStreamMock();
$collection = new StreamCollection([$stream]);
$this->assertCount(1, $collection);
}
public function testGetIterator()
{
$audio = $this->getStreamMock();
$video = $this->getStreamMock();
$collection = new StreamCollection([$audio, $video]);
$this->assertInstanceOf('\Iterator', $collection->getIterator());
$this->assertCount(2, $collection->getIterator());
}
public function testFirst()
{
$stream1 = $this->getStreamMock();
$stream2 = $this->getStreamMock();
$coll = new StreamCollection([$stream1, $stream2]);
$this->assertSame($stream1, $coll->first());
}
}

View file

@ -0,0 +1,136 @@
<?php
namespace Tests\FFMpeg\Unit\FFProbe\DataMapping;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\FFProbe\DataMapping\Stream;
use Tests\FFMpeg\Unit\TestCase;
class StreamTest extends TestCase
{
/**
* @dataProvider provideAudioCases
*/
public function testIsAudio($isAudio, $properties)
{
$stream = new Stream($properties);
$this->assertTrue($isAudio === $stream->isAudio());
}
public function provideAudioCases()
{
return [
[true, ['codec_type' => 'audio']],
[false, ['codec_type' => 'video']],
];
}
/**
* @dataProvider provideVideoCases
*/
public function testIsVideo($isVideo, $properties)
{
$stream = new Stream($properties);
$this->assertTrue($isVideo === $stream->isVideo());
}
public function provideVideoCases()
{
return [
[true, ['codec_type' => 'video']],
[false, ['codec_type' => 'audio']],
];
}
public function testGetDimensionsFromAudio()
{
$this->expectException(
'\FFMpeg\Exception\LogicException',
'Dimensions can only be retrieved from video streams.'
);
$stream = new Stream(['codec_type' => 'audio']);
$stream->getDimensions();
}
public function testGetDimensionsFromVideo()
{
$stream = new Stream(['codec_type' => 'video', 'width' => 960, 'height' => 720]);
$this->assertEquals(new Dimension(960, 720), $stream->getDimensions());
}
/**
* @dataProvider provideInvalidPropertiesForDimensionsExtraction
*/
public function testUnableToGetDimensionsFromVideo($properties)
{
$this->expectException(
'\FFMpeg\Exception\RuntimeException',
'Unable to extract dimensions.'
);
$stream = new Stream(['codec_type' => 'video', 'width' => 960]);
$stream->getDimensions();
}
public function provideInvalidPropertiesForDimensionsExtraction()
{
return [
['codec_type' => 'video', 'width' => 960],
['codec_type' => 'video', 'height' => 960],
];
}
/**
* @dataProvider providePropertiesForDimensionsExtraction
*/
public function testGetDimensionsFromVideoWithDisplayRatio($data)
{
$stream = new Stream([
'codec_type' => 'video',
'width' => $data['width'],
'height' => $data['height'],
'sample_aspect_ratio' => $data['sar'],
'display_aspect_ratio' => $data['dar'],
]);
$this->assertEquals(new Dimension($data['result_width'], $data['result_height']), $stream->getDimensions());
}
/**
* @dataProvider provideInvalidRatios
*/
public function testGetDimensionsFromVideoWithInvalidDisplayRatio($invalidRatio)
{
$stream = new Stream(['codec_type' => 'video', 'width' => 960, 'height' => 720, 'sample_aspect_ratio' => $invalidRatio, 'display_aspect_ratio' => '16:9']);
$this->assertEquals(new Dimension(960, 720), $stream->getDimensions());
}
public function provideInvalidRatios()
{
return [['0:1'], ['2:1:3']];
}
public function providePropertiesForDimensionsExtraction()
{
return [
[
['width' => '960', 'height' => '720',
'sar' => '4:3', 'dar' => '16:9',
'result_width' => '1280', 'result_height' => '720', ],
],
[
['width' => '1920', 'height' => '1080',
'sar' => '1:1', 'dar' => '16:9',
'result_width' => '1920', 'result_height' => '1080', ],
],
[
['width' => '640', 'height' => '480',
'sar' => '75:74', 'dar' => '50:37',
'result_width' => '649', 'result_height' => '480', ],
],
[
['width' => '720', 'height' => '576',
'sar' => '52:28', 'dar' => '16:9',
'result_width' => '1337', 'result_height' => '752', ],
],
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Tests\FFMpeg\Unit\FFProbe;
use FFMpeg\FFProbe;
use FFMpeg\FFProbe\DataMapping\Format;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\FFProbe\Mapper;
use Tests\FFMpeg\Unit\TestCase;
class MapperTest extends TestCase
{
/**
* @dataProvider provideMappings
*/
public function testMap($type, $data, $expected)
{
$mapper = new Mapper();
$this->assertEquals($expected, $mapper->map($type, $data));
}
public function testMapInvalidArgument()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$mapper = new Mapper();
$mapper->map('cool type', 'data');
}
public function provideMappings()
{
$format = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_format.json'), true);
$streams = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_streams.json'), true);
return [
[FFProbe::TYPE_FORMAT, $format, new Format($format['format'])],
[FFProbe::TYPE_STREAMS, $streams, new StreamCollection(array_map(function ($streamData) {
return new Stream($streamData);
}, $streams['streams']))],
];
}
}

View file

@ -0,0 +1,132 @@
<?php
namespace Tests\FFMpeg\Unit\FFProbe;
use FFMpeg\FFProbe\OptionsTester;
use Symfony\Component\Cache\CacheItem;
use Tests\FFMpeg\Unit\TestCase;
class OptionsTesterTest extends TestCase
{
public function testHasOptionWithOldFFProbe()
{
$this->expectException(
'\FFMpeg\Exception\RuntimeException',
'Your FFProbe version is too old and does not support `-help` option, please upgrade.'
);
$cache = $this->getCacheMock();
$executionFailerExceptionMock = $this->getMockBuilder('Alchemy\BinaryDriver\Exception\ExecutionFailureException')
->disableOriginalConstructor()
->getMock();
$ffprobe = $this->getFFProbeDriverMock();
$ffprobe->expects($this->once())
->method('command')
->with(['-help', '-loglevel', 'quiet'])
->will($this->throwException($executionFailerExceptionMock));
$tester = new OptionsTester($ffprobe, $cache);
$tester->has('-print_format');
}
/**
* @dataProvider provideOptions
*/
public function testHasOptionWithCacheEmpty($isPresent, $data, $optionName)
{
$cache = $this->getCacheMock();
$cache->expects($this->exactly(2))
->method('getItem')
->will($this->returnValue(new CacheItem));
$cache->expects($this->exactly(2))
->method('hasItem')
->will($this->returnValue(false));
$cache->expects($this->exactly(2))
->method('save');
$ffprobe = $this->getFFProbeDriverMock();
$ffprobe->expects($this->once())
->method('command')
->with(['-help', '-loglevel', 'quiet'])
->will($this->returnValue($data));
$tester = new OptionsTester($ffprobe, $cache);
$this->assertTrue($isPresent === $tester->has($optionName));
}
public function provideOptions()
{
$data = file_get_contents(__DIR__ . '/../../fixtures/ffprobe/help.raw');
return [
[true, $data, '-print_format'],
[false, $data, '-another_print_format'],
];
}
/**
* @dataProvider provideOptions
*/
public function testHasOptionWithHelpCacheLoaded($isPresent, $data, $optionName)
{
$cache = $this->getCacheMock();
$cacheItem = new CacheItem;
$cacheItem->set($data);
$cache->expects($this->exactly(2))
->method('getItem')
->willReturnOnConsecutiveCalls(
$this->returnValue($cacheItem),
$this->returnValue(new CacheItem)
);
$cache->expects($this->exactly(2))
->method('hasItem')
->willReturnOnConsecutiveCalls(
$this->returnValue(false),
$this->returnValue(true)
);
$cache->expects($this->once())
->method('save');
$ffprobe = $this->getFFProbeDriverMock();
$ffprobe->expects($this->never())
->method('command');
$tester = new OptionsTester($ffprobe, $cache);
$this->assertTrue($isPresent === $tester->has($optionName));
}
/**
* @dataProvider provideOptions
*/
public function testHasOptionWithCacheFullyLoaded($isPresent, $data, $optionName)
{
$cache = $this->getCacheMock();
$cacheItem = new CacheItem();
$cacheItem->set($isPresent);
$cache->expects($this->once())
->method('getItem')
->with(md5('option-' . $optionName))
->will($this->returnValue($cacheItem));
$cache->expects($this->once())
->method('hasItem')
->with(md5('option-' . $optionName))
->will($this->returnValue(true));
$ffprobe = $this->getFFProbeDriverMock();
$ffprobe->expects($this->never())
->method('command');
$tester = new OptionsTester($ffprobe, $cache);
$this->assertTrue($isPresent === $tester->has($optionName));
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Tests\FFMpeg\Unit\FFProbe;
use FFMpeg\FFProbe;
use FFMpeg\FFProbe\OutputParser;
use Tests\FFMpeg\Unit\TestCase;
class OutputParserTest extends TestCase
{
/**
* @dataProvider provideTypeDataAndOutput
*/
public function testParse($type, $data, $expectedOutput)
{
$parser = new OutputParser();
$this->assertEquals($expectedOutput, $parser->parse($type, $data));
}
public function testParseWithInvalidArgument()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$parser = new OutputParser();
$parser->parse('comme ca', 'data');
}
public function provideTypeDataAndOutput()
{
$expectedFormat = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_format.json'), true);
$expectedStreams = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_streams.json'), true);
$rawFormat = file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_format.raw');
$rawStreams = file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_streams.raw');
return [
[FFProbe::TYPE_FORMAT, $rawFormat, $expectedFormat],
[FFProbe::TYPE_STREAMS, $rawStreams, $expectedStreams],
];
}
}

View file

@ -0,0 +1,301 @@
<?php
namespace Tests\FFMpeg\Unit;
use Alchemy\BinaryDriver\Configuration;
use Alchemy\BinaryDriver\ConfigurationInterface;
use FFMpeg\FFProbe;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Process\ExecutableFinder;
class FFProbeTest extends TestCase
{
public function testGetSetParser()
{
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$parser = $this->getFFProbeParserMock();
$ffprobe->setParser($parser);
$this->assertSame($parser, $ffprobe->getParser());
}
public function testGetSetFFProbeDriver()
{
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$driver = $this->getFFProbeDriverMock();
$ffprobe->setFFProbeDriver($driver);
$this->assertSame($driver, $ffprobe->getFFProbeDriver());
}
public function testGetSetFFProbeMapper()
{
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$mapper = $this->getFFProbeMapperMock();
$ffprobe->setMapper($mapper);
$this->assertSame($mapper, $ffprobe->getMapper());
}
public function testGetSetOptionsTester()
{
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$tester = $this->getFFProbeOptionsTesterMock();
$ffprobe->setOptionsTester($tester);
$this->assertSame($tester, $ffprobe->getOptionsTester());
}
public function testGetSetCache()
{
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$cache = $this->getCacheMock();
$ffprobe->setCache($cache);
$this->assertSame($cache, $ffprobe->getCache());
}
public function provideDataWhitoutCache()
{
$stream = $this->getStreamMock();
$format = $this->getFormatMock();
return [
[$stream, 'streams', ['-show_streams', '-print_format'], FFProbe::TYPE_STREAMS, [__FILE__, '-show_streams', '-print_format', 'json'], false],
[$format, 'format', ['-show_format', '-print_format'], FFProbe::TYPE_FORMAT, [__FILE__, '-show_format', '-print_format', 'json'], false],
[$stream, 'streams', ['-show_streams'], FFProbe::TYPE_STREAMS, [__FILE__, '-show_streams'], true],
[$format, 'format', ['-show_format'], FFProbe::TYPE_FORMAT, [__FILE__, '-show_format'], true],
];
}
/**
* @dataProvider provideDataWhitoutCache
*/
public function testProbeWithoutCache($output, $method, $commands, $type, $caughtCommands, $isRaw)
{
$pathfile = __FILE__;
$data = ['key' => 'value'];
$rawData = 'raw data';
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$mapper = $this->getFFProbeMapperMock();
$mapper->expects($this->once())
->method('map')
->with($type, $data)
->will($this->returnValue($output));
$parser = $this->getFFProbeParserMock();
if ($isRaw) {
$parser->expects($this->once())
->method('parse')
->with($type, $rawData)
->will($this->returnValue($data));
} else {
$parser->expects($this->never())
->method('parse');
}
$tester = $this->getFFProbeOptionsTesterMockWithOptions($commands);
$cache = $this->getCacheMock();
$cache->expects($this->once())
->method('hasItem')
->will($this->returnValue(false));
$cache->expects($this->once())
->method('getItem')
->will($this->returnValue(new CacheItem));
$cache->expects($this->once())
->method('save')
->with($this->anything());
$driver = $this->getFFProbeDriverMock();
$driver->expects($this->once())
->method('command')
->with($caughtCommands)
->will($this->returnValue($isRaw ? $rawData : json_encode($data)));
$ffprobe->setOptionsTester($tester)
->setCache($cache)
->setMapper($mapper)
->setFFProbeDriver($driver)
->setParser($parser);
$this->assertEquals($output, call_user_func([$ffprobe, $method], $pathfile));
}
public function provideDataForInvalidJson()
{
$stream = $this->getStreamMock();
$format = $this->getFormatMock();
return [
[$stream, 'streams', ['-show_streams', '-print_format'], FFProbe::TYPE_STREAMS, [__FILE__, '-show_streams', '-print_format', 'json']],
[$format, 'format', ['-show_format', '-print_format'], FFProbe::TYPE_FORMAT, [__FILE__, '-show_format', '-print_format', 'json']],
];
}
/**
* @dataProvider provideDataForInvalidJson
*/
public function testProbeWithWrongJson($output, $method, $commands, $type, $caughtCommands)
{
$pathfile = __FILE__;
$data = ['key' => 'value'];
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$mapper = $this->getFFProbeMapperMock();
$mapper->expects($this->once())
->method('map')
->with($this->isType('string'), 'good data parsed')
->will($this->returnValue($output));
$parser = $this->getFFProbeParserMock();
$parser->expects($this->once())
->method('parse')
->with($this->isType('string', json_encode($data).'lala'))
->will($this->returnValue('good data parsed'));
$tester = $this->getFFProbeOptionsTesterMockWithOptions($commands);
$cache = $this->getCacheMock();
$cache->expects($this->exactly(2))
->method('hasItem')
->will($this->returnValue(false));
$cache->expects($this->once())
->method('getItem')
->will($this->returnValue(new CacheItem));
$driver = $this->getFFProbeDriverMock();
$driver->expects($this->exactly(2))
->method('command')
->will($this->returnValue(json_encode($data).'lala'));
$ffprobe->setOptionsTester($tester)
->setCache($cache)
->setMapper($mapper)
->setFFProbeDriver($driver)
->setParser($parser);
$this->assertEquals($output, call_user_func([$ffprobe, $method], $pathfile));
}
public function provideProbingDataWithCache()
{
$stream = $this->getStreamMock();
$format = $this->getFormatMock();
return [
[$stream, 'streams'],
[$format, 'format'],
];
}
/**
* @dataProvider provideProbingDataWithCache
*/
public function testProbeWithCache($output, $method)
{
$pathfile = __FILE__;
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$mapper = $this->getFFProbeMapperMock();
$mapper->expects($this->never())
->method('map');
$tester = $this->getFFProbeOptionsTesterMock();
$cacheItem = new CacheItem;
$cacheItem->set($output);
$cache = $this->getCacheMock();
$cache->expects($this->once())
->method('hasItem')
->will($this->returnValue(true));
$cache->expects($this->once())
->method('getItem')
->will($this->returnValue($cacheItem));
$cache->expects($this->never())
->method('save');
$driver = $this->getFFProbeDriverMock();
$driver->expects($this->never())
->method('command');
$ffprobe->setOptionsTester($tester)
->setCache($cache)
->setMapper($mapper)
->setFFProbeDriver($driver);
$this->assertEquals($output, call_user_func([$ffprobe, $method], $pathfile));
}
public function provideProbeMethod()
{
return [
['streams'],
['format'],
];
}
/**
* @dataProvider provideProbeMethod
*/
public function testProbeWithoutShowStreamsAvailable($method)
{
$this->expectException('\FFMpeg\Exception\RuntimeException');
$pathfile = __FILE__;
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
$ffprobe->setOptionsTester($this->getFFProbeOptionsTesterMock());
call_user_func([$ffprobe, $method], $pathfile);
}
/**
* @dataProvider provideCreateOptions
*/
public function testCreate($logger, $conf, $cache)
{
$finder = new ExecutableFinder();
$found = false;
foreach (['avprobe', 'ffprobe'] as $name) {
if (null !== $finder->find($name)) {
$found = true;
}
}
if (!$found) {
$this->markTestSkipped('Unable to find avprobe or ffprobe on system');
}
$ffprobe = FFProbe::create();
$this->assertInstanceOf('FFMpeg\FFprobe', $ffprobe);
$ffprobe = FFProbe::create($conf, $logger, $cache);
$this->assertInstanceOf('FFMpeg\FFprobe', $ffprobe);
if (null !== $cache) {
$this->assertSame($cache, $ffprobe->getCache());
}
if (null !== $logger) {
$this->assertSame($logger, $ffprobe->getFFProbeDriver()->getProcessRunner()->getLogger());
}
if ($conf instanceof ConfigurationInterface) {
$this->assertSame($conf, $ffprobe->getFFProbeDriver()->getConfiguration());
}
}
public function provideCreateOptions()
{
return [
[null, ['key' => 'value'], null],
[$this->getLoggerMock(), ['key' => 'value'], null],
[null, new Configuration(), null],
[null, ['key' => 'value'], $this->getCacheMock()],
];
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Audio;
use FFMpeg\Coordinate\TimeCode;
use FFMpeg\Filters\Audio\AudioFilters;
use Tests\FFMpeg\Unit\TestCase;
class AudioClipTest extends TestCase
{
public function testClipping()
{
$capturedFilter = null;
$audio = $this->getAudioMock();
$audio->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AudioClipFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filters = new AudioFilters($audio);
$filters->clip(TimeCode::fromSeconds(5));
$this->assertEquals([0 => '-ss', 1 => '00:00:05.00', 2 => '-acodec', 3 => 'copy'], $capturedFilter->apply($audio, $format));
}
public function testClippingWithDuration()
{
$capturedFilter = null;
$audio = $this->getAudioMock();
$audio->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AudioClipFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filters = new AudioFilters($audio);
$filters->clip(TimeCode::fromSeconds(5), TimeCode::fromSeconds(5));
$this->assertEquals([0 => '-ss', 1 => '00:00:05.00', 2 => '-t', 3 => '00:00:05.00', 4 => '-acodec', 5 => 'copy'], $capturedFilter->apply($audio, $format));
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Audio;
use FFMpeg\Filters\Audio\AudioFilters;
use Tests\FFMpeg\Unit\TestCase;
class AudioFiltersTest extends TestCase
{
public function testResample()
{
$capturedFilter = null;
$audio = $this->getAudioMock();
$audio->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AudioResamplableFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$filters = new AudioFilters($audio);
$filters->resample(8000);
$this->assertEquals(8000, $capturedFilter->getRate());
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Audio;
use FFMpeg\Filters\Audio\AudioFilters;
use Tests\FFMpeg\Unit\TestCase;
class AudioMetadataTest extends TestCase
{
public function testAddMetadata()
{
$capturedFilter = null;
$audio = $this->getAudioMock();
$audio->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AddMetadataFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filters = new AudioFilters($audio);
$filters->addMetadata(['title' => 'Hello World']);
$this->assertEquals([0 => '-metadata', 1 => 'title=Hello World'], $capturedFilter->apply($audio, $format));
}
public function testAddArtwork()
{
$capturedFilter = null;
$audio = $this->getAudioMock();
$audio->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AddMetadataFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filters = new AudioFilters($audio);
$filters->addMetadata(['genre' => 'Some Genre', 'artwork' => '/path/to/file.jpg']);
$this->assertEquals([0 => '-i', 1 => '/path/to/file.jpg', 2 => '-map', 3 => '0', 4 => '-map', 5 => '1', 6 => '-metadata', 7 => 'genre=Some Genre'], $capturedFilter->apply($audio, $format));
$this->assertEquals([0 => '-i', 1 => '/path/to/file.jpg', 2 => '-map', 3 => '0', 4 => '-map', 5 => '1', 6 => '-metadata', 7 => 'genre=Some Genre'], $capturedFilter->apply($audio, $format));
}
public function testRemoveMetadata()
{
$capturedFilter = null;
$audio = $this->getAudioMock();
$audio->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AddMetadataFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filters = new AudioFilters($audio);
$filters->addMetadata();
$this->assertEquals([0 => '-map_metadata', 1 => '-1', 2 => '-vn'], $capturedFilter->apply($audio, $format));
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Audio;
use FFMpeg\Filters\Audio\AudioResamplableFilter;
use Tests\FFMpeg\Unit\TestCase;
class AudioResamplableFilterTest extends TestCase
{
public function testGetRate()
{
$filter = new AudioResamplableFilter(500);
$this->assertEquals(500, $filter->getRate());
}
public function testApply()
{
$audio = $this->getAudioMock();
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filter = new AudioResamplableFilter(500);
$this->assertEquals(['-ac', 2, '-ar', 500], $filter->apply($audio, $format));
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Audio;
use FFMpeg\Filters\Audio\CustomFilter;
use Tests\FFMpeg\Unit\TestCase;
class CustomFilterTest extends TestCase
{
public function testApplyCustomFilter()
{
$audio = $this->getAudioMock();
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$filter = new CustomFilter('whatever i put would end up as a filter');
$this->assertEquals(['-af', 'whatever i put would end up as a filter'], $filter->apply($audio, $format));
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Tests\FFMpeg\Unit\Filters;
use FFMpeg\Filters\Audio\SimpleFilter;
use FFMpeg\Filters\FiltersCollection;
use Tests\FFMpeg\Unit\TestCase;
class FiltersCollectionTest extends TestCase
{
public function testCount()
{
$coll = new FiltersCollection();
$this->assertCount(0, $coll);
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
$this->assertCount(1, $coll);
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
$this->assertCount(2, $coll);
}
public function testIterator()
{
$coll = new FiltersCollection();
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
$this->assertInstanceOf('\ArrayIterator', $coll->getIterator());
$this->assertCount(2, $coll->getIterator());
}
public function testEmptyIterator()
{
$coll = new FiltersCollection();
$this->assertInstanceOf('\ArrayIterator', $coll->getIterator());
}
public function testIteratorSort()
{
$coll = new FiltersCollection();
$coll->add(new SimpleFilter(['a']));
$coll->add(new SimpleFilter(['1'], 12));
$coll->add(new SimpleFilter(['b']));
$coll->add(new SimpleFilter(['2'], 12));
$coll->add(new SimpleFilter(['c']));
$coll->add(new SimpleFilter(['3'], 10));
$coll->add(new SimpleFilter(['d']));
$coll->add(new SimpleFilter(['4'], -2));
$coll->add(new SimpleFilter(['e']));
$data = [];
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
foreach ($coll as $filter) {
$data = array_merge($data, $filter->apply($video, $format));
}
$this->assertEquals(['1', '2', '3', 'a', 'b', 'c', 'd', 'e', '4'], $data);
}
}

View 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(['-vf', 'whatever i put would end up as a filter'], $filter->apply($frame));
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Frame;
use FFMpeg\Coordinate\TimeCode;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Frame\DisplayRatioFixerFilter;
use FFMpeg\Media\Frame;
use Tests\FFMpeg\Unit\TestCase;
class DisplayRatioFixerFilterTest extends TestCase
{
public function testApply()
{
$stream = new Stream(['codec_type' => 'video', 'width' => 960, 'height' => 720]);
$streams = new StreamCollection([$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(['-s', '960x720'], $filter->apply($frame));
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Frame;
use FFMpeg\Filters\Frame\FrameFilters;
use Tests\FFMpeg\Unit\TestCase;
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();
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\Coordinate\Point;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Video\CropFilter;
use Tests\FFMpeg\Unit\TestCase;
class CropFilterTest extends TestCase
{
public function testCommandParamsAreCorrectAndStreamIsUpdated()
{
$stream = new Stream(['width' => 320, 'height' => 240, 'codec_type' => 'video']);
$streams = new StreamCollection([$stream]);
$video = $this->getVideoMock();
$video->expects($this->once())
->method('getStreams')
->will($this->returnValue($streams));
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$dimension = new Dimension(200, 150);
$point = new Point(25, 35);
$filter = new CropFilter($point, $dimension);
$expected = [
'-filter:v',
'crop='.$dimension->getWidth().':'.$dimension->getHeight().':'.$point->getX().':'.$point->getY(),
];
$this->assertEquals($expected, $filter->apply($video, $format));
$this->assertEquals(200, $stream->get('width'));
$this->assertEquals(150, $stream->get('height'));
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Filters\Video\CustomFilter;
use Tests\FFMpeg\Unit\TestCase;
class CustomFilterTest extends TestCase
{
public function testApplyCustomFilter()
{
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$filter = new CustomFilter('whatever i put would end up as a filter');
$this->assertEquals(['-vf', 'whatever i put would end up as a filter'], $filter->apply($video, $format));
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Video\ExtractMultipleFramesFilter;
use Tests\FFMpeg\Unit\TestCase;
class ExtractMultipleFramesFilterTest extends TestCase
{
/**
* @dataProvider provideFrameRates
*/
public function testApply($frameRate, $frameFileType, $destinationFolder, $duration, $modulus, $expected)
{
$video = $this->getVideoMock();
$pathfile = '/path/to/file'.mt_rand();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getModulus')
->will($this->returnValue($modulus));
$streams = new StreamCollection([
new Stream([
'codec_type' => 'video',
'duration' => $duration,
]),
]);
$video->expects($this->once())
->method('getStreams')
->will($this->returnValue($streams));
$filter = new ExtractMultipleFramesFilter($frameRate, $destinationFolder);
$filter->setFrameFileType($frameFileType);
$this->assertEquals($expected, $filter->apply($video, $format));
}
public function provideFrameRates()
{
return [
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/1', '/frame-%03d.jpg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/2', '/frame-%02d.jpg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/5', '/frame-%02d.jpg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/10', '/frame-%02d.jpg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/30', '/frame-%02d.jpg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/60', '/frame-%02d.jpg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/1', '/frame-%03d.jpeg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/2', '/frame-%02d.jpeg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/5', '/frame-%02d.jpeg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/10', '/frame-%02d.jpeg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/30', '/frame-%02d.jpeg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/60', '/frame-%02d.jpeg']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/1', '/frame-%03d.png']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/2', '/frame-%02d.png']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/5', '/frame-%02d.png']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/10', '/frame-%02d.png']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/30', '/frame-%02d.png']],
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/60', '/frame-%02d.png']],
];
}
public function testInvalidFrameFileType()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$filter = new ExtractMultipleFramesFilter('1/1', '/');
$filter->setFrameFileType('webm');
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Coordinate\FrameRate;
use FFMpeg\Filters\Video\FrameRateFilter;
use Tests\FFMpeg\Unit\TestCase;
class FrameRateFilterTest extends TestCase
{
public function testApplyWithAFormatThatSupportsBFrames()
{
$framerate = new FrameRate(54);
$gop = 42;
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('supportBFrames')
->will($this->returnValue(true));
$expected = ['-r', 54, '-b_strategy', '1', '-bf', '3', '-g', 42];
$filter = new FrameRateFilter($framerate, $gop);
$this->assertEquals($expected, $filter->apply($video, $format));
}
public function testApplyWithAFormatThatDoesNotSupportsBFrames()
{
$framerate = new FrameRate(54);
$gop = 42;
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('supportBFrames')
->will($this->returnValue(false));
$expected = ['-r', 54];
$filter = new FrameRateFilter($framerate, $gop);
$this->assertEquals($expected, $filter->apply($video, $format));
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Video\PadFilter;
use Tests\FFMpeg\Unit\TestCase;
class PadFilterTest extends TestCase
{
/**
* @dataProvider provideDimensions
*/
public function testApply(Dimension $dimension, $width, $height, $expected)
{
$video = $this->getVideoMock();
$pathfile = '/path/to/file'.mt_rand();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$streams = new StreamCollection([
new Stream([
'codec_type' => 'video',
'width' => $width,
'height' => $height,
]),
]);
$filter = new PadFilter($dimension);
$this->assertEquals($expected, $filter->apply($video, $format));
}
public function provideDimensions()
{
return [
[new Dimension(1000, 800), 640, 480, ['-vf', 'scale=iw*min(1000/iw\,800/ih):ih*min(1000/iw\,800/ih),pad=1000:800:(1000-iw)/2:(800-ih)/2']],
[new Dimension(300, 600), 640, 480, ['-vf', 'scale=iw*min(300/iw\,600/ih):ih*min(300/iw\,600/ih),pad=300:600:(300-iw)/2:(600-ih)/2']],
[new Dimension(100, 900), 640, 480, ['-vf', 'scale=iw*min(100/iw\,900/ih):ih*min(100/iw\,900/ih),pad=100:900:(100-iw)/2:(900-ih)/2']],
[new Dimension(1200, 200), 640, 480, ['-vf', 'scale=iw*min(1200/iw\,200/ih):ih*min(1200/iw\,200/ih),pad=1200:200:(1200-iw)/2:(200-ih)/2']],
];
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Video\ResizeFilter;
use Tests\FFMpeg\Unit\TestCase;
class ResizeFilterTest extends TestCase
{
/**
* @dataProvider provideDimensions
*/
public function testApply(Dimension $dimension, $mode, $width, $height, $modulus, $expected, $forceStandards = true)
{
$video = $this->getVideoMock();
$pathfile = '/path/to/file'.mt_rand();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getModulus')
->will($this->returnValue($modulus));
$streams = new StreamCollection([
new Stream([
'codec_type' => 'video',
'width' => $width,
'height' => $height,
]),
]);
$video->expects($this->once())
->method('getStreams')
->will($this->returnValue($streams));
$filter = new ResizeFilter($dimension, $mode, $forceStandards);
$this->assertEquals($expected, $filter->apply($video, $format));
}
public function provideDimensions()
{
return [
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_FIT, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_INSET, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_FIT, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_INSET, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_FIT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_INSET, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_FIT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_INSET, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
// test non standard dimension
[new Dimension(700, 150), ResizeFilter::RESIZEMODE_INSET, 123, 456, 2, ['-vf', '[in]scale=62:150 [out]'], true],
[new Dimension(700, 150), ResizeFilter::RESIZEMODE_INSET, 123, 456, 2, ['-vf', '[in]scale=40:150 [out]'], false],
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_FIT, 640, 480, 2, ['-vf', '[in]scale=320:320 [out]']],
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_INSET, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 640, 480, 2, ['-vf', '[in]scale=426:320 [out]']],
];
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Video\RotateFilter;
use Tests\FFMpeg\Unit\TestCase;
class RotateFilterTest extends TestCase
{
/**
* @dataProvider provide90degresTranspositions
*/
public function testApplyWithSizeTransformation($value)
{
$stream = new Stream(['width' => 320, 'height' => 240, 'codec_type' => 'video']);
$streams = new StreamCollection([$stream]);
$video = $this->getVideoMock();
$video->expects($this->once())
->method('getStreams')
->will($this->returnValue($streams));
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$filter = new RotateFilter($value);
$this->assertEquals(['-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 [
[RotateFilter::ROTATE_90],
[RotateFilter::ROTATE_270],
];
}
/**
* @dataProvider provideDegresWithoutTranspositions
*/
public function testApplyWithoutSizeTransformation($value)
{
$video = $this->getVideoMock();
$video->expects($this->never())
->method('getStreams');
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$filter = new RotateFilter($value);
$this->assertEquals(['-vf', $value, '-metadata:s:v:0', 'rotate=0'], $filter->apply($video, $format));
}
public function provideDegresWithoutTranspositions()
{
return [
[RotateFilter::ROTATE_180],
];
}
public function testApplyInvalidAngle()
{
$this->expectException(
'\FFMpeg\Exception\InvalidArgumentException',
'Invalid angle value.'
);
new RotateFilter('90');
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Filters\Video\SynchronizeFilter;
use Tests\FFMpeg\Unit\TestCase;
class SynchronizeFilterTest extends TestCase
{
public function testApply()
{
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$filter = new SynchronizeFilter();
$this->assertEquals(['-async', '1', '-metadata:s:v:0', 'start_time=0'], $filter->apply($video, $format));
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\Filters\Video\ResizeFilter;
use FFMpeg\Filters\Video\VideoFilters;
use Tests\FFMpeg\Unit\TestCase;
class VideoFiltersTest extends TestCase
{
/**
* @dataProvider provideResizeOptions
*/
public function testResize($mode, $forceStandards)
{
$capturedFilter = null;
$video = $this->getVideoMock();
$filters = new VideoFilters($video);
$dimension = $this->getDimensionMock();
$video->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Video\ResizeFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$filters->resize($dimension, $mode, $forceStandards);
$this->assertSame($mode, $capturedFilter->getMode());
$this->assertSame($forceStandards, $capturedFilter->areStandardsForced());
$this->assertSame($dimension, $capturedFilter->getDimension());
}
public function provideResizeOptions()
{
return [
[ResizeFilter::RESIZEMODE_FIT, true],
[ResizeFilter::RESIZEMODE_SCALE_WIDTH, true],
[ResizeFilter::RESIZEMODE_SCALE_HEIGHT, false],
[ResizeFilter::RESIZEMODE_INSET, false],
];
}
public function testResample()
{
$capturedFilter = null;
$video = $this->getVideoMock();
$filters = new VideoFilters($video);
$framerate = $this->getFramerateMock();
$gop = 42;
$video->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Video\FrameRateFilter'))
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
$capturedFilter = $filter;
}));
$filters->framerate($framerate, $gop);
$this->assertSame($framerate, $capturedFilter->getFramerate());
$this->assertSame($gop, $capturedFilter->getGOP());
}
public function testSynchronize()
{
$video = $this->getVideoMock();
$filters = new VideoFilters($video);
$video->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Video\SynchronizeFilter'));
$filters->synchronize();
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Video;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Video\WatermarkFilter;
use Tests\FFMpeg\Unit\TestCase;
class WatermarkFilterTest extends TestCase
{
public function testApplyWatermark()
{
$stream = new Stream(['width' => 320, 'height' => 240, 'codec_type' => 'video']);
$streams = new StreamCollection([$stream]);
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png');
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=0:0 [out]'], $filter->apply($video, $format));
// check size of video is unchanged
$this->assertEquals(320, $stream->get('width'));
$this->assertEquals(240, $stream->get('height'));
}
public function testDifferentCoordinaates()
{
$video = $this->getVideoMock();
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
// test position absolute
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
'position' => 'absolute',
'x' => 10, 'y' => 5,
]);
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=10:5 [out]'], $filter->apply($video, $format));
// test position relative
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
'position' => 'relative',
'bottom' => 10, 'left' => 5,
]);
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=5:main_h - 10 - overlay_h [out]'], $filter->apply($video, $format));
// test position relative
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
'position' => 'relative',
'bottom' => 5, 'right' => 4,
]);
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=main_w - 4 - overlay_w:main_h - 5 - overlay_h [out]'], $filter->apply($video, $format));
// test position relative
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
'position' => 'relative',
'left' => 5, 'top' => 11,
]);
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=5:11 [out]'], $filter->apply($video, $format));
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Waveform;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\Filters\Waveform\WaveformDownmixFilter;
use FFMpeg\Media\Waveform;
use Tests\FFMpeg\Unit\TestCase;
class WaveformDownmixFilterTest extends TestCase
{
public function testApply()
{
$stream = new Stream(['codec_type' => 'audio', 'width' => 960, 'height' => 720]);
$streams = new StreamCollection([$stream]);
$audio = $this->getAudioMock(__FILE__);
$audio->expects($this->once())
->method('getStreams')
->will($this->returnValue($streams));
$waveform = new Waveform($audio, $this->getFFMpegDriverMock(), $this->getFFProbeMock(), 640, 120);
$filter = new WaveformDownmixFilter(true);
$this->assertEquals(['"aformat=channel_layouts=mono"'], $filter->apply($waveform));
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Tests\FFMpeg\Unit\Filters\Waveform;
use FFMpeg\Filters\Waveform\WaveformFilters;
use Tests\FFMpeg\Unit\TestCase;
class WaveformFiltersTest extends TestCase
{
public function testResize()
{
$Waveform = $this->getWaveformMock();
$filters = new WaveformFilters($Waveform);
$Waveform->expects($this->once())
->method('addFilter')
->with($this->isInstanceOf('FFMpeg\Filters\Waveform\WaveformDownmixFilter'));
$filters->setDownmix();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Audio;
use FFMpeg\Format\Audio\Aac;
class AacTest extends AudioTestCase
{
public function getFormat()
{
return new Aac();
}
}

View file

@ -0,0 +1,118 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Audio;
use FFMpeg\Format\Audio\DefaultAudio;
use Tests\FFMpeg\Unit\TestCase;
abstract class AudioTestCase extends TestCase
{
public function testExtraParams()
{
$extraParams = $this->getFormat()->getExtraParams();
$this->assertIsArray($extraParams);
foreach ($extraParams as $param) {
$this->assertScalar($param);
}
}
public function testGetAudioCodec()
{
$this->assertScalar($this->getFormat()->getAudioCodec());
$this->assertContains($this->getFormat()->getAudioCodec(), $this->getFormat()->getAvailableAudioCodecs());
}
public function testSetAudioCodec()
{
$format = $this->getFormat();
foreach ($format->getAvailableAudioCodecs() as $codec) {
$format->setAudioCodec($codec);
$this->assertEquals($codec, $format->getAudioCodec());
}
}
public function testSetInvalidAudioCodec()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$this->getFormat()->setAudioCodec('invalid-random-audio-codec');
}
public function testGetAvailableAudioCodecs()
{
$this->assertGreaterThan(0, count($this->getFormat()->getAvailableAudioCodecs()));
}
public function testGetAudioKiloBitrate()
{
$this->assertIsInt($this->getFormat()->getAudioKiloBitrate());
}
public function testSetAudioKiloBitrate()
{
$format = $this->getFormat();
$format->setAudioKiloBitrate(256);
$this->assertEquals(256, $format->getAudioKiloBitrate());
}
public function testSetInvalidKiloBitrate()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$this->getFormat()->setAudioKiloBitrate(0);
}
public function testSetNegativeKiloBitrate()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$this->getFormat()->setAudioKiloBitrate(-10);
}
public function testGetAudioChannels()
{
$this->assertNull($this->getFormat()->getAudioChannels());
}
public function testSetAudioChannels()
{
$format = $this->getFormat();
$format->setAudioChannels(2);
$this->assertEquals(2, $format->getAudioChannels());
}
public function testSetInvalidChannels()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$this->getFormat()->setAudioChannels(0);
}
public function testSetNegativeChannels()
{
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$this->getFormat()->setAudioChannels(-10);
}
public function testCreateProgressListener()
{
$media = $this->getMockBuilder('FFMpeg\Media\MediaTypeInterface')->getMock();
$media->expects($this->any())
->method('getPathfile')
->will($this->returnValue(__FILE__));
$format = $this->getFormat();
$ffprobe = $this->getFFProbeMock();
foreach ($format->createProgressListener($media, $ffprobe, 1, 3) as $listener) {
$this->assertInstanceOf('FFMpeg\Format\ProgressListener\AudioProgressListener', $listener);
$this->assertSame($ffprobe, $listener->getFFProbe());
$this->assertSame(__FILE__, $listener->getPathfile());
$this->assertSame(1, $listener->getCurrentPass());
$this->assertSame(3, $listener->getTotalPass());
}
}
/**
* @return DefaultAudio
*/
abstract public function getFormat();
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Audio;
use FFMpeg\Format\Audio\Flac;
class FlacTest extends AudioTestCase
{
public function getFormat()
{
return new Flac();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Audio;
use FFMpeg\Format\Audio\Mp3;
class Mp3Test extends AudioTestCase
{
public function getFormat()
{
return new Mp3();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Audio;
use FFMpeg\Format\Audio\Vorbis;
class VorbisTest extends AudioTestCase
{
public function getFormat()
{
return new Vorbis();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Audio;
use FFMpeg\Format\Audio\Wav;
class WavTest extends AudioTestCase
{
public function getFormat()
{
return new Wav();
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace Tests\FFMpeg\Unit\Format\ProgressListener;
use FFMpeg\FFProbe\DataMapping\Format;
use FFMpeg\Format\ProgressListener\AudioProgressListener;
use Tests\FFMpeg\Unit\TestCase;
class AudioProgressListenerTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testHandle(
$size,
$duration,
$data,
$expectedPercent,
$expectedRemaining,
$expectedRate,
$data2,
$expectedPercent2,
$expectedRemaining2,
$expectedRate2,
$currentPass,
$totalPass
) {
$ffprobe = $this->getFFProbeMock();
$ffprobe->expects($this->once())
->method('format')
->with(__FILE__)
->will($this->returnValue(new Format([
'size' => $size,
'duration' => $duration,
])));
$listener = new AudioProgressListener($ffprobe, __FILE__, $currentPass, $totalPass);
$phpunit = $this;
$n = 0;
$listener->on('progress', function ($percent, $remaining, $rate) use (&$n, $phpunit, $expectedPercent, $expectedRemaining, $expectedRate, $expectedPercent2, $expectedRemaining2, $expectedRate2) {
if (0 === $n) {
$phpunit->assertEquals($expectedPercent, $percent);
$phpunit->assertEquals($expectedRemaining, $remaining);
$phpunit->assertEquals($expectedRate, $rate);
} elseif (1 === $n) {
$phpunit->assertEquals($expectedPercent2, $percent);
$phpunit->assertEquals($expectedRemaining2, $remaining);
$phpunit->assertLessThan($expectedRate2 + 3, $rate);
$phpunit->assertGreaterThan($expectedRate2 - 3, $rate);
}
++$n;
});
// first one does not trigger progress event
$listener->handle('any-type'.mt_rand(), $data);
sleep(1);
$listener->handle('any-type'.mt_rand(), $data);
sleep(1);
$listener->handle('any-type'.mt_rand(), $data2);
$this->assertEquals(2, $n);
}
public function provideData()
{
return [
[
2894412,
180.900750,
'size= 712kB time=00:00:45.50 bitrate= 128.1kbits/s',
25,
0,
0,
'size= 1274kB time=00:01:29.32 bitrate= 142.8kbits/s',
49,
2,
563,
1,
1,
],
[
2894412,
180.900750,
'size= 712kB time=00:00:45.50 bitrate= 128.1kbits/s',
12,
0,
0,
'size= 1274kB time=00:01:29.32 bitrate= 142.8kbits/s',
24,
2,
563,
1,
2,
],
];
}
}

View file

@ -0,0 +1,113 @@
<?php
namespace Tests\FFMpeg\Unit\Format\ProgressListener;
use FFMpeg\FFProbe\DataMapping\Format;
use FFMpeg\Format\ProgressListener\VideoProgressListener;
use Tests\FFMpeg\Unit\TestCase;
class VideoProgressListenerTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testHandle(
$size,
$duration,
$newVideoDuration,
$data,
$expectedPercent,
$expectedRemaining,
$expectedRate,
$data2,
$expectedPercent2,
$expectedRemaining2,
$expectedRate2,
$currentPass,
$totalPass
) {
$ffprobe = $this->getFFProbeMock();
$ffprobe->expects($this->once())
->method('format')
->with(__FILE__)
->will($this->returnValue(new Format([
'size' => $size,
'duration' => $duration,
])));
$listener = new VideoProgressListener($ffprobe, __FILE__, $currentPass, $totalPass, $newVideoDuration);
$phpunit = $this;
$n = 0;
$listener->on('progress', function ($percent, $remaining, $rate) use (&$n, $phpunit, $expectedPercent, $expectedRemaining, $expectedRate, $expectedPercent2, $expectedRemaining2, $expectedRate2) {
if (0 === $n) {
$phpunit->assertEquals($expectedPercent, $percent);
$phpunit->assertEquals($expectedRemaining, $remaining);
$phpunit->assertEquals($expectedRate, $rate);
} elseif (1 === $n) {
$phpunit->assertEquals($expectedPercent2, $percent);
$phpunit->assertEquals($expectedRemaining2, $remaining);
$phpunit->assertLessThan($expectedRate2 + 10, $rate);
$phpunit->assertGreaterThan($expectedRate2 - 10, $rate);
}
++$n;
});
// first one does not trigger progress event
$listener->handle('any-type'.mt_rand(), $data);
sleep(1);
$listener->handle('any-type'.mt_rand(), $data);
sleep(1);
$listener->handle('any-type'.mt_rand(), $data2);
$this->assertEquals(2, $n);
}
public function provideData()
{
return [
[
147073958,
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',
2,
0,
0,
'frame= 854 fps=113 q=20.0 size= 4430kB time=00:00:33.04 bitrate=1098.5kbits/s dup=36 drop=0',
11,
32,
3868,
1,
1,
],
[
147073958,
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',
1,
0,
0,
'frame= 854 fps=113 q=20.0 size= 4430kB time=00:00:33.04 bitrate=1098.5kbits/s dup=36 drop=0',
5,
32,
3868,
1,
2,
],
[
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,
],
];
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use FFMpeg\Format\Video\X264;
use Tests\FFMpeg\Unit\TestCase;
class InitialParametersTest extends TestCase
{
public function testApplyInitialParameters()
{
$format = new X264();
$format->setInitialParameters(['-acodec', 'libopus']);
$this->assertEquals(['-acodec', 'libopus'], $format->getInitialParameters());
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use FFMpeg\Format\Video\Ogg;
class OggTest extends VideoTestCase
{
public function getFormat()
{
return new Ogg();
}
}

View file

@ -0,0 +1,90 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use Tests\FFMpeg\Unit\Format\Audio\AudioTestCase;
abstract class VideoTestCase extends AudioTestCase
{
public function testGetVideoCodec()
{
$this->assertScalar($this->getFormat()->getVideoCodec());
$this->assertContains($this->getFormat()->getVideoCodec(), $this->getFormat()->getAvailableVideoCodecs());
}
public function testSupportBFrames()
{
$this->assertIsBool($this->getFormat()->supportBFrames());
}
public function testSetVideoCodec()
{
$format = $this->getFormat();
foreach ($format->getAvailableVideoCodecs() as $codec) {
$format->setVideoCodec($codec);
$this->assertEquals($codec, $format->getVideoCodec());
}
}
public function testGetKiloBitrate()
{
$this->assertIsInt($this->getFormat()->getKiloBitrate());
}
public function testSetKiloBitrate()
{
$format = $this->getFormat();
$format->setKiloBitrate(2560);
$this->assertEquals(2560, $format->getKiloBitrate());
}
public function testSetKiloBitrateBelowZero()
{
$this->expectException('FFMpeg\Exception\InvalidArgumentException');
$format = $this->getFormat();
$format->setKiloBitrate(-1);
}
public function testSetInvalidVideoCodec()
{
$this->expectException('FFMpeg\Exception\InvalidArgumentException');
$this->getFormat()->setVideoCodec('invalid-random-video-codec');
}
public function testGetAvailableVideoCodecs()
{
$this->assertGreaterThan(0, count($this->getFormat()->getAvailableVideoCodecs()));
}
public function testCreateProgressListener()
{
$media = $this->getMockBuilder('FFMpeg\Media\MediaTypeInterface')->getMock();
$media->expects($this->any())
->method('getPathfile')
->will($this->returnValue(__FILE__));
$format = $this->getFormat();
$ffprobe = $this->getFFProbeMock();
foreach ($format->createProgressListener($media, $ffprobe, 1, 3) as $listener) {
$this->assertInstanceOf('FFMpeg\Format\ProgressListener\VideoProgressListener', $listener);
$this->assertSame($ffprobe, $listener->getFFProbe());
$this->assertSame(__FILE__, $listener->getPathfile());
$this->assertSame(1, $listener->getCurrentPass());
$this->assertSame(3, $listener->getTotalPass());
}
}
public function testGetPasses()
{
$this->assertIsInt($this->getFormat()->getPasses());
$this->assertGreaterThan(0, $this->getFormat()->getPasses());
}
public function testGetModulus()
{
$this->assertIsInt($this->getFormat()->getModulus());
$this->assertGreaterThan(0, $this->getFormat()->getModulus());
$this->assertEquals(0, $this->getFormat()->getModulus() % 2);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use FFMpeg\Format\Video\WMV3;
class WMV3Test extends VideoTestCase
{
public function getFormat()
{
return new WMV3();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use FFMpeg\Format\Video\WMV;
class WMVTest extends VideoTestCase
{
public function getFormat()
{
return new WMV();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use FFMpeg\Format\Video\WebM;
class WebMTest extends VideoTestCase
{
public function getFormat()
{
return new WebM();
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\FFMpeg\Unit\Format\Video;
use FFMpeg\Format\Video\X264;
class X264Test extends VideoTestCase
{
public function getFormat()
{
return new X264();
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use Tests\FFMpeg\Unit\TestCase;
abstract class AbstractMediaTestCase extends TestCase
{
}

View file

@ -0,0 +1,38 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
abstract class AbstractStreamableTestCase extends AbstractMediaTestCase
{
public function testGetStreams()
{
$classname = $this->getClassName();
$ffprobe = $this->getFFProbeMock();
$format = $this->getFormatMock();
$ffprobe->expects($this->once())
->method('format')
->with(__FILE__)
->will($this->returnValue($format));
$media = new $classname(__FILE__, $this->getFFMpegDriverMock(), $ffprobe);
$this->assertSame($format, $media->getFormat());
}
public function testGetFormat()
{
$classname = $this->getClassName();
$ffprobe = $this->getFFProbeMock();
$streams = $this->getStreamCollectionMock();
$ffprobe->expects($this->once())
->method('streams')
->with(__FILE__)
->will($this->returnValue($streams));
$media = new $classname(__FILE__, $this->getFFMpegDriverMock(), $ffprobe);
$this->assertSame($streams, $media->getStreams());
}
abstract protected function getClassName();
}

View file

@ -0,0 +1,35 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Media\AdvancedMedia;
class AdvancedMediaTest extends AbstractMediaTestCase
{
public function testGetInputs()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
$this->assertSame([__FILE__, __FILE__], $advancedMedia->getInputs());
}
public function testGetInputsCount()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
$this->assertEquals(2, $advancedMedia->getInputsCount());
}
public function testFiltersReturnFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
$this->assertInstanceOf('FFMpeg\Filters\AdvancedMedia\ComplexFilters', $advancedMedia->filters());
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Format\AudioInterface;
use FFMpeg\Format\ProgressableInterface;
abstract class AudioProg implements ProgressableInterface, AudioInterface
{
}

View file

@ -0,0 +1,341 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Exception\RuntimeException;
use FFMpeg\Media\Audio;
class AudioTest extends AbstractStreamableTestCase
{
public function testFiltersReturnsAudioFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$audio = new Audio(__FILE__, $driver, $ffprobe);
$this->assertInstanceOf('FFMpeg\Filters\Audio\AudioFilters', $audio->filters());
}
public function testAddFiltersAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$audio = new Audio(__FILE__, $driver, $ffprobe);
$audio->setFiltersCollection($filters);
$filter = $this->getMockBuilder('FFMpeg\Filters\Audio\AudioFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$audio->addFilter($filter);
}
public function testAddAVideoFilterThrowsException()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$audio = new Audio(__FILE__, $driver, $ffprobe);
$audio->setFiltersCollection($filters);
$filter = $this->getMockBuilder('FFMpeg\Filters\Video\VideoFilterInterface')->getMock();
$filters->expects($this->never())
->method('add');
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
$audio->addFilter($filter);
}
public function testSaveWithFailure()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$outputPathfile = '/target/file';
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$failure = new RuntimeException('failed to encode');
$driver->expects($this->once())
->method('command')
->will($this->throwException($failure));
$audio = new Audio(__FILE__, $driver, $ffprobe);
$this->expectException('\FFMpeg\Exception\RuntimeException');
$audio->save($format, $outputPathfile);
}
public function testSaveAppliesFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$outputPathfile = '/target/file';
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$audio = new Audio(__FILE__, $driver, $ffprobe);
$filter = $this->getMockBuilder('FFMpeg\Filters\Audio\AudioFilterInterface')->getMock();
$filter->expects($this->once())
->method('apply')
->with($audio, $format)
->will($this->returnValue(['extra-filter-command']));
$capturedCommands = [];
$driver->expects($this->once())
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands) {
$capturedCommands[] = $commands;
}));
$audio->addFilter($filter);
$audio->save($format, $outputPathfile);
foreach ($capturedCommands as $commands) {
$this->assertEquals('-y', $commands[0]);
$this->assertEquals('-i', $commands[1]);
$this->assertEquals(__FILE__, $commands[2]);
$this->assertEquals('extra-filter-command', $commands[3]);
}
}
/**
* @dataProvider provideSaveData
*/
public function testSaveShouldSave($threads, $expectedCommands, $expectedListeners, $format)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$configuration->expects($this->once())
->method('has')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue($threads));
if ($threads) {
$configuration->expects($this->once())
->method('get')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue(24));
} else {
$configuration->expects($this->never())
->method('get');
}
$capturedCommand = $capturedListeners = null;
$driver->expects($this->once())
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommand, &$capturedListeners) {
$capturedCommand = $commands;
$capturedListeners = $listeners;
}));
$outputPathfile = '/target/file';
$audio = new Audio(__FILE__, $driver, $ffprobe);
$audio->save($format, $outputPathfile);
$this->assertEquals($expectedCommands, $capturedCommand);
$this->assertEquals($expectedListeners, $capturedListeners);
}
public function provideSaveData()
{
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$format->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(663));
$format->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(5));
$audioFormat = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$audioFormat->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$audioFormat->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(664));
$audioFormat->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(5));
$audioFormat->expects($this->any())
->method('getAudioCodec')
->will($this->returnValue('patati-patata-audio'));
$formatExtra = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$formatExtra->expects($this->any())
->method('getExtraParams')
->will($this->returnValue(['extra', 'param']));
$formatExtra->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(665));
$formatExtra->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(5));
$listeners = [$this->getMockBuilder('Alchemy\BinaryDriver\Listeners\ListenerInterface')->getMock()];
$progressableFormat = $this->getMockBuilder('Tests\FFMpeg\Unit\Media\AudioProg')
->disableOriginalConstructor()->getMock();
$progressableFormat->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$progressableFormat->expects($this->any())
->method('createProgressListener')
->will($this->returnValue($listeners));
$progressableFormat->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(666));
$progressableFormat->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(5));
return [
[false, [
'-y', '-i', __FILE__,
'-b:a', '663k',
'-ac', '5',
'/target/file',
], null, $format],
[false, [
'-y', '-i', __FILE__,
'-acodec', 'patati-patata-audio',
'-b:a', '664k',
'-ac', '5',
'/target/file',
], null, $audioFormat],
[false, [
'-y', '-i', __FILE__,
'extra', 'param',
'-b:a', '665k',
'-ac', '5',
'/target/file',
], null, $formatExtra],
[true, [
'-y', '-i', __FILE__,
'-threads', 24,
'-b:a', '663k',
'-ac', '5',
'/target/file',
], null, $format],
[true, [
'-y', '-i', __FILE__,
'extra', 'param',
'-threads', 24,
'-b:a', '665k',
'-ac', '5',
'/target/file',
], null, $formatExtra],
[false, [
'-y', '-i', __FILE__,
'-b:a', '666k',
'-ac', '5',
'/target/file',
], $listeners, $progressableFormat],
[true, [
'-y', '-i', __FILE__,
'-threads', 24,
'-b:a', '666k',
'-ac', '5',
'/target/file',
], $listeners, $progressableFormat],
];
}
public function testSaveShouldNotStoreCodecFiltersInTheMedia()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$configuration->expects($this->any())
->method('has')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue(true));
$configuration->expects($this->any())
->method('get')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue(24));
$capturedCommands = [];
$driver->expects($this->exactly(2))
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands, &$capturedListeners) {
$capturedCommands[] = $commands;
}));
$outputPathfile = '/target/file';
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue(['param']));
$audio = new Audio(__FILE__, $driver, $ffprobe);
$audio->save($format, $outputPathfile);
$audio->save($format, $outputPathfile);
$expected = [
'-y', '-i', __FILE__, 'param', '-threads', 24, '/target/file',
];
foreach ($capturedCommands as $capturedCommand) {
$this->assertEquals($expected, $capturedCommand);
}
}
public function getClassName()
{
return 'FFMpeg\Media\Audio';
}
}

View 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->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getPasses')
->will($this->returnValue(1));
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$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 [
['SS01', null, [
'-y', '-ss', 'SS01',
'-i', __FILE__, ],
],
['SS02', 'D02', [
'-y', '-ss', 'SS02',
'-i', __FILE__,
'-t', 'D02', ],
],
];
}
}

View file

@ -0,0 +1,149 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Media\Concat;
use Spatie\TemporaryDirectory\TemporaryDirectory;
class ConcatTest extends AbstractMediaTestCase
{
public function testGetSources()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$concat = new Concat([__FILE__, __FILE__], $driver, $ffprobe);
$this->assertSame([__FILE__, __FILE__], $concat->getSources());
}
public function testFiltersReturnFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$concat = new Concat([__FILE__, __FILE__], $driver, $ffprobe);
$this->assertInstanceOf('FFMpeg\Filters\Concat\ConcatFilters', $concat->filters());
}
public function testAddFiltersAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$filter = $this->getMockBuilder('FFMpeg\Filters\Concat\ConcatFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$concat = new Concat([__FILE__, __FILE__], $driver, $ffprobe);
$concat->setFiltersCollection($filters);
$concat->addFilter($filter);
}
/**
* @dataProvider provideSaveFromSameCodecsOptions
*/
public function testSaveFromSameCodecs($streamCopy, $commands)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$pathfile = '/target/destination';
array_push($commands, $pathfile);
$driver->expects($this->exactly(1))
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) {
}));
$concat = new Concat([__FILE__, 'concat-2.mp4'], $driver, $ffprobe);
$concat->saveFromSameCodecs($pathfile, $streamCopy);
$this->assertEquals('-f', $commands[0]);
$this->assertEquals('concat', $commands[1]);
$this->assertEquals('-safe', $commands[2]);
$this->assertEquals('0', $commands[3]);
$this->assertEquals('-i', $commands[4]);
if (isset($commands[6]) && (0 == strcmp($commands[6], '-c'))) {
$this->assertEquals('-c', $commands[6]);
$this->assertEquals('copy', $commands[7]);
}
}
public function provideSaveFromSameCodecsOptions()
{
$fs = (new TemporaryDirectory())->create();
$tmpFile = $fs->path('ffmpeg-concat');
touch($tmpFile);
return [
[
true,
[
'-f', 'concat',
'-safe', '0',
'-i', $tmpFile,
'-c', 'copy',
],
],
[
false,
[
'-f', 'concat',
'-safe', '0',
'-i', $tmpFile,
],
],
];
}
/**
* @dataProvider provideSaveFromDifferentCodecsOptions
*/
public function testSaveFromDifferentCodecs($commands)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$format = $this->getFormatInterfaceMock();
$pathfile = '/target/destination';
array_push($commands, $pathfile);
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$driver->expects($this->once())
->method('command')
->with($commands);
$concat = new Concat([__FILE__, 'concat-2.mp4'], $driver, $ffprobe);
$this->assertSame($concat, $concat->saveFromDifferentCodecs($format, $pathfile));
}
public function provideSaveFromDifferentCodecsOptions()
{
return [
[
[
'-i', __FILE__,
'-i', 'concat-2.mp4',
'-filter_complex',
'[0:v:0] [0:a:0] [1:v:0] [1:a:0] concat=n=2:v=1:a=1 [v] [a]',
'-map', '[v]',
'-map', '[a]',
],
],
];
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Media\Frame;
class FrameTest extends AbstractMediaTestCase
{
public function testGetTimeCode()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode);
$this->assertSame($timecode, $frame->getTimeCode());
}
public function testFiltersReturnFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode);
$this->assertInstanceOf('FFMpeg\Filters\Frame\FrameFilters', $frame->filters());
}
public function testAddFiltersAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$filter = $this->getMockBuilder('FFMpeg\Filters\Frame\FrameFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode);
$frame->setFiltersCollection($filters);
$frame->addFilter($filter);
}
/**
* @dataProvider provideSaveOptions
*/
public function testSave($accurate, $base64, $commands)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$timecode->expects($this->once())
->method('__toString')
->will($this->returnValue('timecode'));
$pathfile = '/target/destination';
if (!$base64) {
array_push($commands, $pathfile);
}
$driver->expects($this->once())
->method('command')
->with($commands);
if (!$base64) {
$frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode);
$this->assertSame($frame, $frame->save($pathfile, $accurate, $base64));
} else {
$frame = new Frame($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode);
$frame->save($pathfile, $accurate, $base64);
}
}
public function provideSaveOptions()
{
return [
[false, false, [
'-y', '-ss', 'timecode',
'-i', __FILE__,
'-vframes', '1',
'-f', 'image2', ],
],
[true, false, [
'-y', '-i', __FILE__,
'-vframes', '1', '-ss', 'timecode',
'-f', 'image2', ],
],
[false, true, [
'-y', '-ss', 'timecode',
'-i', __FILE__,
'-vframes', '1',
'-f', 'image2pipe', '-', ],
],
[true, true, [
'-y', '-i', __FILE__,
'-vframes', '1', '-ss', 'timecode',
'-f', 'image2pipe', '-', ],
],
];
}
}

View file

@ -0,0 +1,116 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Coordinate\Dimension;
use FFMpeg\Media\Gif;
class GifTest extends AbstractMediaTestCase
{
public function testGetTimeCode()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$dimension = $this->getDimensionMock();
$gif = new Gif($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode, $dimension);
$this->assertSame($timecode, $gif->getTimeCode());
}
public function testGetDimension()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$dimension = $this->getDimensionMock();
$gif = new Gif($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode, $dimension);
$this->assertSame($dimension, $gif->getDimension());
}
public function testFiltersReturnFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$dimension = $this->getDimensionMock();
$gif = new Gif($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode, $dimension);
$this->assertInstanceOf('FFMpeg\Filters\Gif\GifFilters', $gif->filters());
}
public function testAddFiltersAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$dimension = $this->getDimensionMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$filter = $this->getMockBuilder('FFMpeg\Filters\Gif\GifFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$gif = new Gif($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode, $dimension);
$gif->setFiltersCollection($filters);
$gif->addFilter($filter);
}
/**
* @dataProvider provideSaveOptions
*/
public function testSave($dimension, $duration, $commands)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$timecode = $this->getTimeCodeMock();
$timecode->expects($this->once())
->method('__toString')
->will($this->returnValue('timecode'));
$pathfile = '/target/destination';
array_push($commands, $pathfile);
$driver->expects($this->once())
->method('command')
->with($commands);
$gif = new Gif($this->getVideoMock(__FILE__), $driver, $ffprobe, $timecode, $dimension, $duration);
$this->assertSame($gif, $gif->save($pathfile));
}
public function provideSaveOptions()
{
return [
[
new Dimension(320, 240), 3,
[
'-ss', 'timecode',
'-t', '3',
'-i', __FILE__,
'-vf',
'scale=320:-1', '-gifflags',
'+transdiff', '-y',
],
],
[
new Dimension(320, 240), null,
[
'-ss', 'timecode',
'-i', __FILE__,
'-vf',
'scale=320:-1', '-gifflags',
'+transdiff', '-y',
],
],
];
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Format\ProgressableInterface;
use FFMpeg\Format\VideoInterface;
abstract class Prog implements ProgressableInterface, VideoInterface
{
}

View file

@ -0,0 +1,713 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Exception\RuntimeException;
use FFMpeg\Format\Video\X264;
use FFMpeg\Media\Video;
class VideoTest extends AbstractStreamableTestCase
{
public function testFiltersReturnsVideoFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$video = new Video(__FILE__, $driver, $ffprobe);
$this->assertInstanceOf('FFMpeg\Filters\Video\VideoFilters', $video->filters());
}
public function testAddFiltersAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$video = new Video(__FILE__, $driver, $ffprobe);
$video->setFiltersCollection($filters);
$filter = $this->getMockBuilder('FFMpeg\Filters\Video\VideoFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$video->addFilter($filter);
}
public function testAddAudioFilterAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$video = new Video(__FILE__, $driver, $ffprobe);
$video->setFiltersCollection($filters);
$filter = $this->getMockBuilder('FFMpeg\Filters\Audio\AudioFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$video->addFilter($filter);
}
public function testFrameShouldReturnAFrame()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$at = $this->getTimeCodeMock();
$video = new Video(__FILE__, $driver, $ffprobe);
$frame = $video->frame($at);
$this->assertInstanceOf('FFMpeg\Media\Frame', $frame);
$this->assertSame($at, $frame->getTimeCode());
$this->assertSame(__FILE__, $frame->getPathfile());
}
public function testSaveWithFailure()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$outputPathfile = '/target/file';
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getPasses')
->will($this->returnValue(1));
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$failure = new RuntimeException('failed to encode');
$driver->expects($this->once())
->method('command')
->will($this->throwException($failure));
$video = new Video(__FILE__, $driver, $ffprobe);
$this->expectException('\FFMpeg\Exception\RuntimeException');
$video->save($format, $outputPathfile);
}
public function testSaveAppliesFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$outputPathfile = '/target/file';
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$format->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$video = new Video(__FILE__, $driver, $ffprobe);
$filter = $this->getMockBuilder('FFMpeg\Filters\Video\VideoFilterInterface')->getMock();
$filter->expects($this->once())
->method('apply')
->with($video, $format)
->will($this->returnValue(['extra-filter-command']));
$capturedCommands = [];
$driver->expects($this->exactly(2))
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands) {
$capturedCommands[] = $commands;
}));
$video->addFilter($filter);
$video->save($format, $outputPathfile);
foreach ($capturedCommands as $commands) {
$this->assertEquals('-y', $commands[0]);
$this->assertEquals('-i', $commands[1]);
$this->assertEquals(__FILE__, $commands[2]);
$this->assertEquals('extra-filter-command', $commands[3]);
}
}
/**
* @dataProvider provideSaveData
*/
public function testSaveShouldSave($threads, $expectedCommands, $expectedListeners, $format)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$configuration->expects($this->once())
->method('has')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue($threads));
if ($threads) {
$configuration->expects($this->once())
->method('get')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue(24));
} else {
$configuration->expects($this->never())
->method('get');
}
$capturedCommands = [];
$capturedListeners = null;
$driver->expects($this->exactly(count($expectedCommands)))
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands, &$capturedListeners) {
$capturedCommands[] = $commands;
$capturedListeners = $listeners;
}));
$outputPathfile = '/target/file';
$video = new Video(__FILE__, $driver, $ffprobe);
$video->save($format, $outputPathfile);
foreach ($capturedCommands as $passKey => $pass) {
$prefix = null;
if (count($expectedCommands) > 1) {
// look for pass commands only in multipass cases
foreach ($pass as $command) {
$prefix = null;
if (false !== strpos($command, '/pass-')) {
$prefix = $command;
break;
}
}
if (null === $prefix) {
$this->fail('Unable to find pass prefix command.');
}
}
$found = false || (null === $prefix);
foreach ($pass as $key => $command) {
if ($command === $prefix) {
$found = true;
unset($capturedCommands[$passKey][$key]);
$capturedCommands[$passKey] = array_values($capturedCommands[$passKey]);
break;
}
}
if (!$found) {
$this->fail('Unable to find pass prefix command back.');
}
}
$this->assertEquals($expectedCommands, $capturedCommands);
$this->assertEquals($expectedListeners, $capturedListeners);
}
public function provideSaveData()
{
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$format->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(663));
$format->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$format->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$format->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$format->expects($this->any())
->method('getAdditionalParameters')
->will($this->returnValue(['foo', 'bar']));
$format2 = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format2->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$format2->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(663));
$format2->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$format2->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$format2->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$format2->expects($this->any())
->method('getAdditionalParameters')
->will($this->returnValue(['foo', 'bar']));
$audioFormat = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
$audioFormat->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$audioFormat->expects($this->any())
->method('getAudioCodec')
->will($this->returnValue('patati-patata-audio'));
$audioFormat->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$audioFormat->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$audioFormat->expects($this->any())
->method('getPasses')
->will($this->returnValue(1));
$audioVideoFormat = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$audioVideoFormat->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$audioVideoFormat->expects($this->any())
->method('getVideoCodec')
->will($this->returnValue('gloubi-boulga-video'));
$audioVideoFormat->expects($this->any())
->method('getAudioCodec')
->will($this->returnValue('patati-patata-audio'));
$audioVideoFormat->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(664));
$audioVideoFormat->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$audioVideoFormat->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$audioVideoFormat->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$audioVideoFormat->expects($this->any())
->method('getAdditionalParameters')
->will($this->returnValue([]));
$audioVideoFormatSinglePass = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$audioVideoFormatSinglePass->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$audioVideoFormatSinglePass->expects($this->any())
->method('getVideoCodec')
->will($this->returnValue('gloubi-boulga-video'));
$audioVideoFormatSinglePass->expects($this->any())
->method('getAudioCodec')
->will($this->returnValue('patati-patata-audio'));
$audioVideoFormatSinglePass->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(664));
$audioVideoFormatSinglePass->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$audioVideoFormatSinglePass->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$audioVideoFormatSinglePass->expects($this->any())
->method('getPasses')
->will($this->returnValue(1));
$audioVideoFormatSinglePass->expects($this->any())
->method('getAdditionalParameters')
->will($this->returnValue([]));
$formatExtra = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$formatExtra->expects($this->any())
->method('getExtraParams')
->will($this->returnValue(['extra', 'param']));
$formatExtra->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(665));
$formatExtra->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$formatExtra->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$formatExtra->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$formatExtra->expects($this->any())
->method('getAdditionalParameters')
->will($this->returnValue([]));
$formatExtra2 = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$formatExtra2->expects($this->any())
->method('getExtraParams')
->will($this->returnValue(['extra', 'param']));
$formatExtra2->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(665));
$formatExtra2->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$formatExtra2->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$formatExtra2->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$formatExtra2->expects($this->any())
->method('getAdditionalParameters')
->will($this->returnValue([]));
$listeners = [$this->getMockBuilder('Alchemy\BinaryDriver\Listeners\ListenerInterface')->getMock()];
$progressableFormat = $this->getMockBuilder('Tests\FFMpeg\Unit\Media\Prog')
->disableOriginalConstructor()->getMock();
$progressableFormat->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$progressableFormat->expects($this->any())
->method('createProgressListener')
->will($this->returnValue($listeners));
$progressableFormat->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(666));
$progressableFormat->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$progressableFormat->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$progressableFormat->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$progressableFormat2 = $this->getMockBuilder('Tests\FFMpeg\Unit\Media\Prog')
->disableOriginalConstructor()->getMock();
$progressableFormat2->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$progressableFormat2->expects($this->any())
->method('createProgressListener')
->will($this->returnValue($listeners));
$progressableFormat2->expects($this->any())
->method('getKiloBitrate')
->will($this->returnValue(666));
$progressableFormat2->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$progressableFormat2->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$progressableFormat2->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$progressableAudioFormat = $this->getMockBuilder('Tests\FFMpeg\Unit\Media\AudioProg')
->disableOriginalConstructor()->getMock();
$progressableAudioFormat->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
$progressableAudioFormat->expects($this->any())
->method('getAudioCodec')
->will($this->returnValue('patati-patata-audio'));
$progressableAudioFormat->expects($this->any())
->method('createProgressListener')
->will($this->returnValue($listeners));
$progressableAudioFormat->expects($this->any())
->method('getAudioKiloBitrate')
->will($this->returnValue(92));
$progressableAudioFormat->expects($this->any())
->method('getAudioChannels')
->will($this->returnValue(2));
$progressableAudioFormat->expects($this->any())
->method('getPasses')
->will($this->returnValue(1));
return [
[false, [[
'-y', '-i', __FILE__, '-b:v', '663k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', 2, 'foo', 'bar', '-pass', 1, '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'-b:v', '663k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', 2, 'foo', 'bar', '-pass', 2, '-passlogfile',
'/target/file',
]], null, $format],
[false, [[
'-y', '-i', __FILE__,
'-vcodec', 'gloubi-boulga-video',
'-acodec', 'patati-patata-audio', '-b:v', '664k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '1', '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'-vcodec', 'gloubi-boulga-video',
'-acodec', 'patati-patata-audio',
'-b:v', '664k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '2', '-passlogfile',
'/target/file',
]], null, $audioVideoFormat],
[false, [[
'-y', '-i', __FILE__,
'-vcodec', 'gloubi-boulga-video',
'-acodec', 'patati-patata-audio', '-b:v', '664k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2',
'/target/file',
]], null, $audioVideoFormatSinglePass],
[false, [[
'-y', '-i', __FILE__,
'extra', 'param', '-b:v', '665k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '1', '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'extra', 'param', '-b:v', '665k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '2', '-passlogfile',
'/target/file',
]], null, $formatExtra],
[true, [[
'-y', '-i', __FILE__,
'-threads', 24, '-b:v', '663k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', 2, 'foo', 'bar', '-pass', 1, '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'-threads', 24,
'-b:v', '663k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', 2, 'foo', 'bar', '-pass', 2, '-passlogfile',
'/target/file',
]], null, $format2],
[true, [[
'-y', '-i', __FILE__,
'extra', 'param', '-threads', 24, '-b:v', '665k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '1', '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'extra', 'param', '-threads', 24, '-b:v', '665k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '2', '-passlogfile',
'/target/file',
]], null, $formatExtra2],
[false, [[
'-y', '-i', __FILE__, '-b:v', '666k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '1', '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'-b:v', '666k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '2', '-passlogfile',
'/target/file',
]], $listeners, $progressableFormat2],
[true, [[
'-y', '-i', __FILE__,
'-threads', 24, '-b:v', '666k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '1', '-passlogfile',
'/target/file',
], [
'-y', '-i', __FILE__,
'-threads', 24,
'-b:v', '666k',
'-refs', '6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71', '-qcomp', '0.6',
'-qdiff', '4', '-trellis', '1', '-b:a', '92k', '-ac', '2', '-pass', '2', '-passlogfile',
'/target/file',
]], $listeners, $progressableFormat],
[true, [[
'-y', '-i', __FILE__,
'-threads', 24, '-acodec', 'patati-patata-audio',
'-b:a', '92k', '-ac', '2',
'/target/file',
]], null, $audioFormat],
[true, [[
'-y', '-i', __FILE__,
'-threads', 24, '-acodec', 'patati-patata-audio',
'-b:a', '92k', '-ac', '2',
'/target/file',
]], $listeners, $progressableAudioFormat],
];
}
public function testSaveShouldNotStoreCodecFiltersInTheMedia()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$configuration->expects($this->any())
->method('has')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue(true));
$configuration->expects($this->any())
->method('get')
->with($this->equalTo('ffmpeg.threads'))
->will($this->returnValue(24));
$capturedCommands = [];
$driver->expects($this->exactly(4))
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands, &$capturedListeners) {
$capturedCommands[] = $commands;
}));
$outputPathfile = '/target/file';
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
$format->expects($this->any())
->method('getExtraParams')
->will($this->returnValue(['param']));
$format->expects($this->any())
->method('getPasses')
->will($this->returnValue(2));
$video = new Video(__FILE__, $driver, $ffprobe);
$video->save($format, $outputPathfile);
$video->save($format, $outputPathfile);
$expectedPass1 = [
'-y', '-i', __FILE__, 'param', '-threads', 24, '-b:v', 'k', '-refs',
'6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71',
'-qcomp', '0.6', '-qdiff', '4', '-trellis', '1',
'-pass', '1', '-passlogfile', '/target/file',
];
$expectedPass2 = [
'-y', '-i', __FILE__, 'param', '-threads', 24, '-b:v', 'k', '-refs',
'6', '-coder', '1', '-sc_threshold', '40', '-flags', '+loop',
'-me_range', '16', '-subq', '7', '-i_qfactor', '0.71',
'-qcomp', '0.6', '-qdiff', '4', '-trellis', '1',
'-pass', '2', '-passlogfile', '/target/file',
];
$n = 1;
foreach ($capturedCommands as $capturedCommand) {
$prefix = null;
foreach ($capturedCommand as $command) {
if (false !== strpos($command, '/pass-')) {
$prefix = $command;
break;
}
}
if (null === $prefix) {
$this->fail('Unable to find pass prefix command.');
}
$found = false;
foreach ($capturedCommand as $key => $command) {
if ($command === $prefix) {
$found = true;
unset($capturedCommand[$key]);
$capturedCommand = array_values($capturedCommand);
break;
}
}
if (!$found) {
$this->fail('Unable to find pass prefix command back.');
}
if (0 === $n % 2) {
$this->assertEquals($expectedPass2, $capturedCommand);
} else {
$this->assertEquals($expectedPass1, $capturedCommand);
}
++$n;
}
}
public function testCaseWhereKiloBitRateIsEqualToZero()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$pathfile = '/target/destination';
$outputPathfile = '/target/file';
$format = new X264();
$format->setKiloBitrate(0);
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
$driver->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($configuration));
$self = $this;
$driver->expects($this->exactly(1))
->method('command')
->with($this->isType('array'), false, $this->anything())
->will($this->returnCallback(function ($commands, $errors, $listeners) use ($self) {
$self->assertTrue(!in_array('-b:v', $commands));
}));
$video = new Video(__FILE__, $driver, $ffprobe);
$video->save($format, $outputPathfile);
}
public function getClassName()
{
return 'FFMpeg\Media\Video';
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace Tests\FFMpeg\Unit\Media;
use FFMpeg\Media\Waveform;
class WaveformTest extends AbstractMediaTestCase
{
public function testFiltersReturnFilters()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$waveform = new Waveform($this->getAudioMock(__FILE__), $driver, $ffprobe, 640, 120);
$this->assertInstanceOf('FFMpeg\Filters\Waveform\WaveformFilters', $waveform->filters());
}
public function testAddFiltersAddsAFilter()
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
->disableOriginalConstructor()
->getMock();
$filter = $this->getMockBuilder('FFMpeg\Filters\Waveform\WaveformFilterInterface')->getMock();
$filters->expects($this->once())
->method('add')
->with($filter);
$waveform = new Waveform($this->getAudioMock(__FILE__), $driver, $ffprobe, 640, 120);
$waveform->setFiltersCollection($filters);
$waveform->addFilter($filter);
}
/**
* @dataProvider provideSaveOptions
*/
public function testSave($commands)
{
$driver = $this->getFFMpegDriverMock();
$ffprobe = $this->getFFProbeMock();
$pathfile = '/tests/files/Audio.mp3';
array_push($commands, $pathfile);
$driver->expects($this->once())
->method('command')
->with($commands);
$waveform = new Waveform($this->getAudioMock(__FILE__), $driver, $ffprobe, 640, 120, array('#FFFFFF'));
$this->assertSame($waveform, $waveform->save($pathfile));
}
public function provideSaveOptions()
{
return [
[
[
'-y', '-i', null, '-filter_complex',
'showwavespic=colors=#FFFFFF:s=640x120',
'-frames:v', '1',
],
],
];
}
}

View file

@ -0,0 +1,168 @@
<?php
namespace Tests\FFMpeg\Unit;
use Tests\FFMpeg\BaseTestCase;
class TestCase extends BaseTestCase
{
public function getLoggerMock()
{
return $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
}
public function getCacheMock()
{
return $this->getMockBuilder('Psr\Cache\CacheItemPoolInterface')->getMock();
}
public function getTimeCodeMock()
{
return $this->getMockBuilder('FFMpeg\Coordinate\TimeCode')
->disableOriginalConstructor()
->getMock();
}
public function getDimensionMock()
{
return $this->getMockBuilder('FFMpeg\Coordinate\Dimension')
->disableOriginalConstructor()
->getMock();
}
public function getFramerateMock()
{
return $this->getMockBuilder('FFMpeg\Coordinate\Framerate')
->disableOriginalConstructor()
->getMock();
}
public function getFrameMock()
{
return $this->getMockBuilder('FFMpeg\Media\Frame')
->disableOriginalConstructor()
->getMock();
}
public function getWaveformMock()
{
return $this->getMockBuilder('FFMpeg\Media\Waveform')
->disableOriginalConstructor()
->getMock();
}
public function getFFMpegDriverMock()
{
return $this->getMockBuilder('FFMpeg\Driver\FFMpegDriver')
->disableOriginalConstructor()
->getMock();
}
public function getFFProbeDriverMock()
{
return $this->getMockBuilder('FFMpeg\Driver\FFProbeDriver')
->disableOriginalConstructor()
->getMock();
}
public function getFFProbeMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe')
->disableOriginalConstructor()
->getMock();
}
public function getStreamMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe\DataMapping\Stream')
->disableOriginalConstructor()
->getMock();
}
public function getFFProbeParserMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe\OutputParserInterface')->getMock();
}
public function getFFProbeOptionsTesterMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe\OptionsTesterInterface')->getMock();
}
public function getFFProbeMapperMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe\MapperInterface')->getMock();
}
public function getFFProbeOptionsTesterMockWithOptions(array $options)
{
$tester = $this->getFFProbeOptionsTesterMock();
$tester->expects($this->any())
->method('has')
->will($this->returnCallback(function ($option) use ($options) {
return in_array($option, $options);
}));
return $tester;
}
public function getConfigurationMock()
{
return $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
}
public function getFormatMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe\DataMapping\Format')
->disableOriginalConstructor()
->getMock();
}
public function getStreamCollectionMock()
{
return $this->getMockBuilder('FFMpeg\FFProbe\DataMapping\StreamCollection')
->disableOriginalConstructor()
->getMock();
}
protected function getAudioMock()
{
return $this->getMockBuilder('FFMpeg\Media\Audio')
->disableOriginalConstructor()
->getMock();
}
protected function getVideoMock($filename = null)
{
$video = $this->getMockBuilder('FFMpeg\Media\Video')
->disableOriginalConstructor()
->getMock();
$video->expects($this->any())
->method('getPathfile')
->will($this->returnValue($filename));
return $video;
}
public function getConcatMock()
{
return $this->getMockBuilder('FFMpeg\Media\Concat')
->disableOriginalConstructor()
->getMock();
}
public function getFormatInterfaceMock()
{
$FormatInterface = $this->getMockBuilder('FFMpeg\Format\FormatInterface')
->disableOriginalConstructor()
->getMock();
$FormatInterface->expects($this->any())
->method('getExtraParams')
->will($this->returnValue([]));
return $FormatInterface;
}
}