[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:
parent
72c946dc7d
commit
111c153428
335 changed files with 4394 additions and 28116 deletions
300
tests/FFMpeg/Functional/AdvancedMediaTest.php
Normal file
300
tests/FFMpeg/Functional/AdvancedMediaTest.php
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\Filters\AdvancedMedia\TestSrcFilter;
|
||||
use FFMpeg\Filters\AdvancedMedia\XStackFilter;
|
||||
use FFMpeg\Format\Audio\Mp3;
|
||||
use FFMpeg\Format\Video\X264;
|
||||
|
||||
class AdvancedMediaTest extends FunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* Path prefix to avoid conflicts with another tests.
|
||||
*/
|
||||
public const OUTPUT_PATH_PREFIX = 'output/advanced_media_';
|
||||
|
||||
public function testRunWithoutComplexFilterTestExtractAudio()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$format = new Mp3();
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'extracted_with_map.mp3';
|
||||
|
||||
// You can run it without -filter_complex, just using -map.
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia
|
||||
->map(['0:a'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'MP2/3 (MPEG audio layer 2/3)',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testAudio()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Audio.mp3')];
|
||||
$format = new Mp3();
|
||||
$format->setAudioKiloBitrate(30);
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'audio_test.mp3';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia
|
||||
->map(['0:a'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'MP2/3 (MPEG audio layer 2/3)',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testMultipleInputs()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [
|
||||
realpath(__DIR__.'/../files/portrait.MOV'),
|
||||
realpath(__DIR__.'/../files/portrait.MOV'),
|
||||
];
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'multiple_inputs_test.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->custom('[0:v][1:v]', 'hstack', '[v]');
|
||||
$advancedMedia
|
||||
->map(['0:a', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FFMpeg\Media\AdvancedMedia::map
|
||||
*/
|
||||
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.
|
||||
$inputs = [];
|
||||
$formatX264 = new X264('aac', 'libx264');
|
||||
$formatMp3 = new Mp3();
|
||||
|
||||
$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';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->sine('[a]', 5)
|
||||
->testSrc('[v1]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->testSrc('[v2]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->custom('[v1]', 'negate', '[v1negate]')
|
||||
->custom('[v2]', 'edgedetect', '[v2edgedetect]');
|
||||
$advancedMedia
|
||||
->map(['[a]'], $formatMp3, $outputMp3)
|
||||
->map(['[v1negate]'], $formatX264, $outputVideo1)
|
||||
->map(['[v2edgedetect]'], $formatX264, $outputVideo2)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($outputMp3);
|
||||
$this->assertEquals(
|
||||
'MP2/3 (MPEG audio layer 2/3)',
|
||||
$ffmpeg->open($outputMp3)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($outputMp3);
|
||||
|
||||
$this->assertFileExists($outputVideo1);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($outputVideo1)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($outputVideo1);
|
||||
|
||||
$this->assertFileExists($outputVideo2);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($outputVideo2)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($outputVideo2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\TestSrcFilter
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\SineFilter
|
||||
*/
|
||||
public function testTestSrcFilterTestSineFilter()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'testsrc.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->sine('[a]', 10)
|
||||
->testSrc('[v]', TestSrcFilter::TESTSRC, '160x120', 10);
|
||||
$advancedMedia
|
||||
->map(['[a]', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* XStack filter is supported starting from 4.1 ffmpeg version.
|
||||
*
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\XStackFilter
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\SineFilter
|
||||
*/
|
||||
public function testXStackFilter()
|
||||
{
|
||||
$xStack = new XStackFilter('', 0);
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$ffmpegVersion = $ffmpeg->getFFMpegDriver()->getVersion();
|
||||
if (version_compare($ffmpegVersion, $xStack->getMinimalFFMpegVersion(), '<')) {
|
||||
$this->markTestSkipped('XStack filter is supported starting from ffmpeg version '
|
||||
.$xStack->getMinimalFFMpegVersion().', your version is '
|
||||
.$ffmpegVersion);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'xstack_test.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->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)
|
||||
->xStack(
|
||||
'[v1][v2][v3][v4]',
|
||||
XStackFilter::LAYOUT_2X2,
|
||||
4,
|
||||
'[v]'
|
||||
);
|
||||
$advancedMedia
|
||||
->map(['[a]', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testOfCompatibilityWithExistedFilters()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$watermark = realpath(__DIR__.'/../files/watermark.png');
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_of_compatibility_with_existed_filters.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
// 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]');
|
||||
$advancedMedia
|
||||
->map(['0:a', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testForceDisableAudio()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$format = new X264();
|
||||
|
||||
$advancedMedia1 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia1
|
||||
->map(['test'], $format, 'outputFile.mp4', false);
|
||||
$this->assertStringContainsString('acodec', $advancedMedia1->getFinalCommand());
|
||||
|
||||
$advancedMedia2 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia2
|
||||
->map(['test'], $format, 'outputFile.mp4', true);
|
||||
$this->assertStringNotContainsString('acodec', $advancedMedia2->getFinalCommand());
|
||||
}
|
||||
|
||||
public function testForceDisableVideo()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$format = new X264();
|
||||
|
||||
$advancedMedia1 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia1->map(
|
||||
['test'],
|
||||
$format,
|
||||
'outputFile.mp4',
|
||||
false,
|
||||
false
|
||||
);
|
||||
$this->assertStringContainsString('vcodec', $advancedMedia1->getFinalCommand());
|
||||
|
||||
$advancedMedia2 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia2->map(
|
||||
['test'],
|
||||
$format,
|
||||
'outputFile.mp4',
|
||||
false,
|
||||
true
|
||||
);
|
||||
$this->assertStringNotContainsString('vcodec', $advancedMedia2->getFinalCommand());
|
||||
}
|
||||
|
||||
public function testGlobalOptions()
|
||||
{
|
||||
$configuration = [
|
||||
'ffmpeg.threads' => 3,
|
||||
'ffmpeg.filter_threads' => 13,
|
||||
'ffmpeg.filter_complex_threads' => 24,
|
||||
];
|
||||
|
||||
$ffmpeg = $this->getFFMpeg($configuration);
|
||||
$advancedMedia = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$command = $advancedMedia->getFinalCommand();
|
||||
|
||||
foreach ($configuration as $optionName => $optionValue) {
|
||||
$optionName = str_replace('ffmpeg.', '', $optionName);
|
||||
$this->assertStringContainsString('-'.$optionName.' '.$optionValue, $command);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
tests/FFMpeg/Functional/AudioConcatenationTest.php
Normal file
28
tests/FFMpeg/Functional/AudioConcatenationTest.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
class AudioConcatenationTest extends FunctionalTestCase
|
||||
{
|
||||
public function testSimpleAudioFileConcatTest()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
|
||||
$files = [
|
||||
realpath(__DIR__ . '/../files/Jahzzar_-_05_-_Siesta.mp3'),
|
||||
realpath(__DIR__ . '/../files/02_-_Favorite_Secrets.mp3'),
|
||||
];
|
||||
|
||||
$audio = $ffmpeg->open(reset($files));
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\Media\Audio', $audio);
|
||||
|
||||
clearstatcache();
|
||||
$filename = __DIR__ . '/output/concat-output.mp3';
|
||||
|
||||
$audio->concat($files)->saveFromSameCodecs($filename, true);
|
||||
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
40
tests/FFMpeg/Functional/FFProbeTest.php
Normal file
40
tests/FFMpeg/Functional/FFProbeTest.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\FFProbe;
|
||||
|
||||
class FFProbeTest extends FunctionalTestCase
|
||||
{
|
||||
public function testProbeOnFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertGreaterThan(0, count($ffprobe->streams(__DIR__.'/../files/Audio.mp3')));
|
||||
}
|
||||
|
||||
public function testValidateExistingFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertTrue($ffprobe->isValid(__DIR__.'/../files/sample.3gp'));
|
||||
}
|
||||
|
||||
public function testValidateNonExistingFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertFalse($ffprobe->isValid(__DIR__.'/../files/WrongFile.mp4'));
|
||||
}
|
||||
|
||||
public function testProbeOnNonExistantFile()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
|
||||
$ffprobe = FFProbe::create();
|
||||
$ffprobe->streams('/path/to/no/file');
|
||||
}
|
||||
|
||||
public function testProbeOnRemoteFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertGreaterThan(0, count($ffprobe->streams('http://vjs.zencdn.net/v/oceans.mp4')));
|
||||
}
|
||||
}
|
||||
19
tests/FFMpeg/Functional/FunctionalTestCase.php
Normal file
19
tests/FFMpeg/Functional/FunctionalTestCase.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\FFMpeg;
|
||||
use Tests\FFMpeg\BaseTestCase;
|
||||
|
||||
abstract class FunctionalTestCase extends BaseTestCase
|
||||
{
|
||||
/**
|
||||
* @param array $configuration
|
||||
*
|
||||
* @return FFMpeg
|
||||
*/
|
||||
public function getFFMpeg($configuration = [])
|
||||
{
|
||||
return FFMpeg::create(array_merge(['timeout' => 300], $configuration));
|
||||
}
|
||||
}
|
||||
146
tests/FFMpeg/Functional/VideoTranscodeTest.php
Normal file
146
tests/FFMpeg/Functional/VideoTranscodeTest.php
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\Filters\Video\ResizeFilter;
|
||||
use FFMpeg\Filters\Video\RotateFilter;
|
||||
use FFMpeg\Format\Video\X264;
|
||||
use FFMpeg\Media\Video;
|
||||
|
||||
class VideoTranscodeTest extends FunctionalTestCase
|
||||
{
|
||||
public function testSimpleTranscodeX264()
|
||||
{
|
||||
$filename = __DIR__.'/output/output-x264.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__.'/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__.'/../files/Test.ogv');
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\Media\Video', $video);
|
||||
|
||||
$lastPercentage = null;
|
||||
$phpunit = $this;
|
||||
|
||||
$codec = new X264('aac');
|
||||
$codec->on('progress', function ($video, $codec, $percentage) use ($phpunit, &$lastPercentage) {
|
||||
if (null !== $lastPercentage) {
|
||||
$phpunit->assertGreaterThanOrEqual($lastPercentage, $percentage);
|
||||
}
|
||||
$lastPercentage = $percentage;
|
||||
$phpunit->assertGreaterThanOrEqual(0, $percentage);
|
||||
$phpunit->assertLessThanOrEqual(100, $percentage);
|
||||
});
|
||||
|
||||
$video->save($codec, $filename);
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
public function testAacTranscodeX264()
|
||||
{
|
||||
$filename = __DIR__.'/output/output-x264_2.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__.'/output/output-x264_2.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__.'/../files/sample.3gp');
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\Media\Video', $video);
|
||||
|
||||
$lastPercentage = null;
|
||||
$phpunit = $this;
|
||||
|
||||
$codec = new X264('aac');
|
||||
$codec->on('progress', function ($video, $codec, $percentage) use ($phpunit, &$lastPercentage) {
|
||||
if (null !== $lastPercentage) {
|
||||
$phpunit->assertGreaterThanOrEqual($lastPercentage, $percentage);
|
||||
}
|
||||
$lastPercentage = $percentage;
|
||||
$phpunit->assertGreaterThanOrEqual(0, $percentage);
|
||||
$phpunit->assertLessThanOrEqual(100, $percentage);
|
||||
});
|
||||
|
||||
$video->save($codec, $filename);
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
public function testTranscodeInvalidFile()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$ffmpeg->open(__DIR__.'/../files/UnknownFileTest.ogv');
|
||||
}
|
||||
|
||||
public function testSaveInvalidForgedVideo()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = new Video(__DIR__.'/../files/UnknownFileTest.ogv', $ffmpeg->getFFMpegDriver(), $ffmpeg->getFFProbe());
|
||||
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
$video->save(new X264('aac'), __DIR__.'/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
public function testTranscodePortraitVideo()
|
||||
{
|
||||
$info = $this->getNameAndVersion();
|
||||
|
||||
if ('avconv' === $info['name'] && version_compare($info['version'], '0.9', '<')) {
|
||||
$this->markTestSkipped('This version of avconv is buggy and does not support this test.');
|
||||
}
|
||||
|
||||
$filename = __DIR__.'/output/output-x264.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__.'/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__.'/../files/portrait.MOV');
|
||||
|
||||
$video->filters()
|
||||
->resize(new Dimension(320, 240), ResizeFilter::RESIZEMODE_INSET)
|
||||
->rotate(RotateFilter::ROTATE_90);
|
||||
$video->save(new X264('aac'), $filename);
|
||||
|
||||
$dimension = $ffmpeg->getFFProbe()
|
||||
->streams($filename)
|
||||
->videos()
|
||||
->first()
|
||||
->getDimensions();
|
||||
|
||||
$this->assertLessThan(1, $dimension->getRatio(false)->getValue());
|
||||
$this->assertEquals(240, $dimension->getHeight());
|
||||
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
private function getNameAndVersion()
|
||||
{
|
||||
$binary = $this
|
||||
->getFFMpeg()
|
||||
->getFFMpegDriver()
|
||||
->getProcessBuilderFactory()
|
||||
->getBinary();
|
||||
|
||||
$output = $matches = null;
|
||||
exec($binary.' -version 2>&1', $output);
|
||||
|
||||
if (!isset($output[0])) {
|
||||
return ['name' => null, 'version' => null];
|
||||
}
|
||||
|
||||
preg_match('/^([a-z]+)\s+version\s+([0-9\.]+)/i', $output[0], $matches);
|
||||
|
||||
if (count($matches) > 0) {
|
||||
return ['name' => $matches[1], 'version' => $matches[2]];
|
||||
}
|
||||
|
||||
return ['name' => null, 'version' => null];
|
||||
}
|
||||
}
|
||||
0
tests/FFMpeg/Functional/output/.placeholder
Normal file
0
tests/FFMpeg/Functional/output/.placeholder
Normal file
Loading…
Add table
Add a link
Reference in a new issue