2020-02-18 13:49:32 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Tests\FFMpeg\Functional;
|
|
|
|
|
|
|
|
|
|
use FFMpeg\Coordinate\Dimension;
|
2020-03-23 17:47:36 +03:00
|
|
|
use FFMpeg\Filters\AdvancedMedia\TestSrcFilter;
|
|
|
|
|
use FFMpeg\Filters\AdvancedMedia\XStackFilter;
|
2020-02-18 13:49:32 +03:00
|
|
|
use FFMpeg\Format\Audio\Mp3;
|
|
|
|
|
use FFMpeg\Format\Video\X264;
|
|
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
class AdvancedMediaTest extends FunctionalTestCase
|
2020-02-18 13:49:32 +03:00
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Path prefix to avoid conflicts with another tests.
|
|
|
|
|
*/
|
2022-02-09 14:32:43 +01:00
|
|
|
public const OUTPUT_PATH_PREFIX = 'output/advanced_media_';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
|
|
|
|
public function testRunWithoutComplexFilterTestExtractAudio()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
2020-02-18 13:49:32 +03:00
|
|
|
$format = new Mp3();
|
2022-02-09 14:32:43 +01:00
|
|
|
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'extracted_with_map.mp3';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
|
|
|
|
// You can run it without -filter_complex, just using -map.
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['0:a'], $format, $output)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($output);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'MP2/3 (MPEG audio layer 2/3)',
|
|
|
|
|
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAudio()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [realpath(__DIR__.'/../files/Audio.mp3')];
|
2020-02-18 13:49:32 +03:00
|
|
|
$format = new Mp3();
|
|
|
|
|
$format->setAudioKiloBitrate(30);
|
2022-02-09 14:32:43 +01:00
|
|
|
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'audio_test.mp3';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['0:a'], $format, $output)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($output);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'MP2/3 (MPEG audio layer 2/3)',
|
|
|
|
|
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testMultipleInputs()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [
|
|
|
|
|
realpath(__DIR__.'/../files/portrait.MOV'),
|
|
|
|
|
realpath(__DIR__.'/../files/portrait.MOV'),
|
|
|
|
|
];
|
2020-02-18 13:49:32 +03:00
|
|
|
$format = new X264('aac', 'libx264');
|
2022-02-09 14:32:43 +01:00
|
|
|
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'multiple_inputs_test.mp4';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia->filters()
|
2020-02-18 13:49:32 +03:00
|
|
|
->custom('[0:v][1:v]', 'hstack', '[v]');
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['0:a', '[v]'], $format, $output)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($output);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'QuickTime / MOV',
|
|
|
|
|
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-03-23 17:47:36 +03:00
|
|
|
* @covers \FFMpeg\Media\AdvancedMedia::map
|
2020-02-18 13:49:32 +03:00
|
|
|
*/
|
|
|
|
|
public function testMultipleOutputsTestAbsenceOfInputs()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
|
|
|
|
// in this test we use only computed inputs
|
|
|
|
|
// and can ignore -i part of the command, pass empty inputs array.
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [];
|
2020-02-18 13:49:32 +03:00
|
|
|
$formatX264 = new X264('aac', 'libx264');
|
|
|
|
|
$formatMp3 = new Mp3();
|
|
|
|
|
|
2022-02-09 14:32:43 +01:00
|
|
|
$outputMp3 = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_multiple_outputs.mp3';
|
|
|
|
|
$outputVideo1 = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_multiple_outputs_v1.mp4';
|
|
|
|
|
$outputVideo2 = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_multiple_outputs_v2.mp4';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia->filters()
|
2020-02-18 13:49:32 +03:00
|
|
|
->sine('[a]', 5)
|
|
|
|
|
->testSrc('[v1]', TestSrcFilter::TESTSRC, '160x120', 5)
|
|
|
|
|
->testSrc('[v2]', TestSrcFilter::TESTSRC, '160x120', 5)
|
|
|
|
|
->custom('[v1]', 'negate', '[v1negate]')
|
|
|
|
|
->custom('[v2]', 'edgedetect', '[v2edgedetect]');
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['[a]'], $formatMp3, $outputMp3)
|
|
|
|
|
->map(['[v1negate]'], $formatX264, $outputVideo1)
|
|
|
|
|
->map(['[v2edgedetect]'], $formatX264, $outputVideo2)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($outputMp3);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'MP2/3 (MPEG audio layer 2/3)',
|
|
|
|
|
$ffmpeg->open($outputMp3)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($outputMp3);
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($outputVideo1);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'QuickTime / MOV',
|
|
|
|
|
$ffmpeg->open($outputVideo1)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($outputVideo1);
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($outputVideo2);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'QuickTime / MOV',
|
|
|
|
|
$ffmpeg->open($outputVideo2)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($outputVideo2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-03-23 17:47:36 +03:00
|
|
|
* @covers \FFMpeg\Filters\AdvancedMedia\TestSrcFilter
|
|
|
|
|
* @covers \FFMpeg\Filters\AdvancedMedia\SineFilter
|
2020-02-18 13:49:32 +03:00
|
|
|
*/
|
|
|
|
|
public function testTestSrcFilterTestSineFilter()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
2020-02-18 13:49:32 +03:00
|
|
|
$format = new X264('aac', 'libx264');
|
2022-02-09 14:32:43 +01:00
|
|
|
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'testsrc.mp4';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia->filters()
|
2020-02-18 13:49:32 +03:00
|
|
|
->sine('[a]', 10)
|
|
|
|
|
->testSrc('[v]', TestSrcFilter::TESTSRC, '160x120', 10);
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['[a]', '[v]'], $format, $output)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($output);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'QuickTime / MOV',
|
|
|
|
|
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* XStack filter is supported starting from 4.1 ffmpeg version.
|
|
|
|
|
*
|
2020-03-23 17:47:36 +03:00
|
|
|
* @covers \FFMpeg\Filters\AdvancedMedia\XStackFilter
|
|
|
|
|
* @covers \FFMpeg\Filters\AdvancedMedia\SineFilter
|
2020-02-18 13:49:32 +03:00
|
|
|
*/
|
|
|
|
|
public function testXStackFilter()
|
|
|
|
|
{
|
2020-02-26 12:33:45 +03:00
|
|
|
$xStack = new XStackFilter('', 0);
|
2020-02-18 13:49:32 +03:00
|
|
|
$ffmpeg = $this->getFFMpeg();
|
2020-02-26 12:33:45 +03:00
|
|
|
$ffmpegVersion = $ffmpeg->getFFMpegDriver()->getVersion();
|
|
|
|
|
if (version_compare($ffmpegVersion, $xStack->getMinimalFFMpegVersion(), '<')) {
|
|
|
|
|
$this->markTestSkipped('XStack filter is supported starting from ffmpeg version '
|
2022-02-09 14:32:43 +01:00
|
|
|
.$xStack->getMinimalFFMpegVersion().', your version is '
|
|
|
|
|
.$ffmpegVersion);
|
|
|
|
|
|
2020-02-20 14:34:21 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
2020-02-18 13:49:32 +03:00
|
|
|
$format = new X264('aac', 'libx264');
|
2022-02-09 14:32:43 +01:00
|
|
|
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'xstack_test.mp4';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia->filters()
|
2020-02-18 13:49:32 +03:00
|
|
|
->sine('[a]', 5)
|
|
|
|
|
->testSrc('[v1]', TestSrcFilter::TESTSRC, '160x120', 5)
|
|
|
|
|
->testSrc('[v2]', TestSrcFilter::TESTSRC, '160x120', 5)
|
|
|
|
|
->testSrc('[v3]', TestSrcFilter::TESTSRC, '160x120', 5)
|
|
|
|
|
->testSrc('[v4]', TestSrcFilter::TESTSRC, '160x120', 5)
|
2022-02-09 14:32:43 +01:00
|
|
|
->xStack(
|
|
|
|
|
'[v1][v2][v3][v4]',
|
|
|
|
|
XStackFilter::LAYOUT_2X2,
|
|
|
|
|
4,
|
|
|
|
|
'[v]'
|
|
|
|
|
);
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['[a]', '[v]'], $format, $output)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($output);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'QuickTime / MOV',
|
|
|
|
|
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testOfCompatibilityWithExistedFilters()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
2022-02-09 14:32:43 +01:00
|
|
|
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
|
|
|
|
$watermark = realpath(__DIR__.'/../files/watermark.png');
|
2020-02-18 13:49:32 +03:00
|
|
|
$format = new X264('aac', 'libx264');
|
2022-02-09 14:32:43 +01:00
|
|
|
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_of_compatibility_with_existed_filters.mp4';
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
|
|
|
|
$advancedMedia->filters()
|
2020-02-18 13:49:32 +03:00
|
|
|
// For unknown reasons WatermarkFilter produce an error on Windows,
|
|
|
|
|
// because the path to the watermark becomes corrupted.
|
|
|
|
|
// This behaviour related with Alchemy\BinaryDriver\AbstractBinary::command().
|
|
|
|
|
// The path inside filter becomes like
|
|
|
|
|
// "D:ServerswwwPHP-FFMpegtestsfileswatermark.png" (without slashes).
|
|
|
|
|
// But on Linux systems filter works as expected.
|
|
|
|
|
//->watermark('[0:v]', $watermark, '[v]')
|
|
|
|
|
->pad('[0:v]', new Dimension(300, 100), '[v]');
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['0:a', '[v]'], $format, $output)
|
2020-02-18 13:49:32 +03:00
|
|
|
->save();
|
|
|
|
|
|
|
|
|
|
$this->assertFileExists($output);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'QuickTime / MOV',
|
|
|
|
|
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
|
|
|
|
);
|
2020-02-18 13:49:32 +03:00
|
|
|
unlink($output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testForceDisableAudio()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
|
|
|
|
$format = new X264();
|
|
|
|
|
|
2022-02-09 14:32:43 +01:00
|
|
|
$advancedMedia1 = $ffmpeg->openAdvanced([__FILE__]);
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia1
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['test'], $format, 'outputFile.mp4', false);
|
2020-03-23 17:47:36 +03:00
|
|
|
$this->assertStringContainsString('acodec', $advancedMedia1->getFinalCommand());
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2022-02-09 14:32:43 +01:00
|
|
|
$advancedMedia2 = $ffmpeg->openAdvanced([__FILE__]);
|
2020-03-23 17:47:36 +03:00
|
|
|
$advancedMedia2
|
2022-02-09 14:32:43 +01:00
|
|
|
->map(['test'], $format, 'outputFile.mp4', true);
|
2020-03-23 17:47:36 +03:00
|
|
|
$this->assertStringNotContainsString('acodec', $advancedMedia2->getFinalCommand());
|
2020-02-18 13:49:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testForceDisableVideo()
|
|
|
|
|
{
|
|
|
|
|
$ffmpeg = $this->getFFMpeg();
|
|
|
|
|
$format = new X264();
|
|
|
|
|
|
2022-02-09 14:32:43 +01:00
|
|
|
$advancedMedia1 = $ffmpeg->openAdvanced([__FILE__]);
|
|
|
|
|
$advancedMedia1->map(
|
|
|
|
|
['test'],
|
|
|
|
|
$format,
|
|
|
|
|
'outputFile.mp4',
|
|
|
|
|
false,
|
|
|
|
|
false
|
|
|
|
|
);
|
2020-03-23 17:47:36 +03:00
|
|
|
$this->assertStringContainsString('vcodec', $advancedMedia1->getFinalCommand());
|
2020-02-18 13:49:32 +03:00
|
|
|
|
2022-02-09 14:32:43 +01:00
|
|
|
$advancedMedia2 = $ffmpeg->openAdvanced([__FILE__]);
|
|
|
|
|
$advancedMedia2->map(
|
|
|
|
|
['test'],
|
|
|
|
|
$format,
|
|
|
|
|
'outputFile.mp4',
|
|
|
|
|
false,
|
|
|
|
|
true
|
|
|
|
|
);
|
2020-03-23 17:47:36 +03:00
|
|
|
$this->assertStringNotContainsString('vcodec', $advancedMedia2->getFinalCommand());
|
2020-02-18 13:49:32 +03:00
|
|
|
}
|
2020-03-02 14:50:42 +03:00
|
|
|
|
|
|
|
|
public function testGlobalOptions()
|
|
|
|
|
{
|
2022-02-09 14:32:43 +01:00
|
|
|
$configuration = [
|
2020-03-02 14:50:42 +03:00
|
|
|
'ffmpeg.threads' => 3,
|
|
|
|
|
'ffmpeg.filter_threads' => 13,
|
|
|
|
|
'ffmpeg.filter_complex_threads' => 24,
|
2022-02-09 14:32:43 +01:00
|
|
|
];
|
2020-03-02 14:50:42 +03:00
|
|
|
|
|
|
|
|
$ffmpeg = $this->getFFMpeg($configuration);
|
2022-02-09 14:32:43 +01:00
|
|
|
$advancedMedia = $ffmpeg->openAdvanced([__FILE__]);
|
2020-03-23 17:47:36 +03:00
|
|
|
$command = $advancedMedia->getFinalCommand();
|
2020-03-02 14:50:42 +03:00
|
|
|
|
|
|
|
|
foreach ($configuration as $optionName => $optionValue) {
|
|
|
|
|
$optionName = str_replace('ffmpeg.', '', $optionName);
|
2022-02-09 14:32:43 +01:00
|
|
|
$this->assertStringContainsString('-'.$optionName.' '.$optionValue, $command);
|
2020-03-02 14:50:42 +03:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-18 13:49:32 +03:00
|
|
|
}
|