Added the ComplexMedia type and the possibility to use -filter_complex and -map features. Added some built-in complex filters. Added the unit and functional tests of the new features. README.md has been updated.
This commit is contained in:
		
					parent
					
						
							
								89b0c2b4d0
							
						
					
				
			
			
				commit
				
					
						f20ad8a82e
					
				
			
		
					 17 changed files with 1583 additions and 7 deletions
				
			
		
							
								
								
									
										241
									
								
								tests/Functional/ComplexMediaTest.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								tests/Functional/ComplexMediaTest.php
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,241 @@ | |||
| <?php | ||||
| 
 | ||||
| namespace Tests\FFMpeg\Functional; | ||||
| 
 | ||||
| use FFMpeg\Coordinate\Dimension; | ||||
| use FFMpeg\Filters\ComplexMedia\TestSrcFilter; | ||||
| use FFMpeg\Filters\ComplexMedia\XStackFilter; | ||||
| use FFMpeg\Format\Audio\Mp3; | ||||
| use FFMpeg\Format\Video\X264; | ||||
| 
 | ||||
| class ComplexMediaTest extends FunctionalTestCase | ||||
| { | ||||
|     /** | ||||
|      * Path prefix to avoid conflicts with another tests. | ||||
|      */ | ||||
|     const PATH_PREFIX = 'complex_media_'; | ||||
| 
 | ||||
|     public function testRunWithoutComplexFilterTestExtractAudio() | ||||
|     { | ||||
|         $ffmpeg = $this->getFFMpeg(); | ||||
|         $inputs = array(realpath(__DIR__ . '/../files/Test.ogv')); | ||||
|         $format = new Mp3(); | ||||
|         $output = __DIR__ . '/output/' . self::PATH_PREFIX . 'extracted_with_map.mp3'; | ||||
| 
 | ||||
|         // You can run it without -filter_complex, just using -map.
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia | ||||
|             ->map(array('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 = array(realpath(__DIR__ . '/../files/Audio.mp3')); | ||||
|         $format = new Mp3(); | ||||
|         $format->setAudioKiloBitrate(30); | ||||
|         $output = __DIR__ . '/output/' . self::PATH_PREFIX . 'audio_test.mp3'; | ||||
| 
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia | ||||
|             ->map(array('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 = array( | ||||
|             realpath(__DIR__ . '/../files/portrait.MOV'), | ||||
|             realpath(__DIR__ . '/../files/portrait.MOV') | ||||
|         ); | ||||
|         $format = new X264('aac', 'libx264'); | ||||
|         $output = __DIR__ . '/output/' . self::PATH_PREFIX . 'multiple_inputs_test.mp4'; | ||||
| 
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia->filters() | ||||
|             ->custom('[0:v][1:v]', 'hstack', '[v]'); | ||||
|         $complexMedia | ||||
|             ->map(array('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\ComplexMedia::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 = array(); | ||||
|         $formatX264 = new X264('aac', 'libx264'); | ||||
|         $formatMp3 = new Mp3(); | ||||
| 
 | ||||
|         $outputMp3 = __DIR__ . '/output/' . self::PATH_PREFIX . 'test_multiple_outputs.mp3'; | ||||
|         $outputVideo1 = __DIR__ . '/output/' . self::PATH_PREFIX . 'test_multiple_outputs_v1.mp4'; | ||||
|         $outputVideo2 = __DIR__ . '/output/' . self::PATH_PREFIX . 'test_multiple_outputs_v2.mp4'; | ||||
| 
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia->filters() | ||||
|             ->sine('[a]', 5) | ||||
|             ->testSrc('[v1]', TestSrcFilter::TESTSRC, '160x120', 5) | ||||
|             ->testSrc('[v2]', TestSrcFilter::TESTSRC, '160x120', 5) | ||||
|             ->custom('[v1]', 'negate', '[v1negate]') | ||||
|             ->custom('[v2]', 'edgedetect', '[v2edgedetect]'); | ||||
|         $complexMedia | ||||
|             ->map(array('[a]'), $formatMp3, $outputMp3) | ||||
|             ->map(array('[v1negate]'), $formatX264, $outputVideo1) | ||||
|             ->map(array('[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\ComplexMedia\TestSrcFilter | ||||
|      * @covers \FFMpeg\Filters\ComplexMedia\SineFilter | ||||
|      */ | ||||
|     public function testTestSrcFilterTestSineFilter() | ||||
|     { | ||||
|         $ffmpeg = $this->getFFMpeg(); | ||||
|         $inputs = array(realpath(__DIR__ . '/../files/Test.ogv')); | ||||
|         $format = new X264('aac', 'libx264'); | ||||
|         $output = __DIR__ . '/output/' . self::PATH_PREFIX . 'testsrc.mp4'; | ||||
| 
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia->filters() | ||||
|             ->sine('[a]', 10) | ||||
|             ->testSrc('[v]', TestSrcFilter::TESTSRC, '160x120', 10); | ||||
|         $complexMedia | ||||
|             ->map(array('[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\ComplexMedia\XStackFilter | ||||
|      * @covers \FFMpeg\Filters\ComplexMedia\SineFilter | ||||
|      */ | ||||
|     public function testXStackFilter() | ||||
|     { | ||||
|         $ffmpeg = $this->getFFMpeg(); | ||||
|         $inputs = array(realpath(__DIR__ . '/../files/Test.ogv')); | ||||
|         $format = new X264('aac', 'libx264'); | ||||
|         $output = __DIR__ . '/output/' . self::PATH_PREFIX . 'xstack_test.mp4'; | ||||
| 
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia->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]'); | ||||
|         $complexMedia | ||||
|             ->map(array('[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 = array(realpath(__DIR__ . '/../files/Test.ogv')); | ||||
|         $watermark = realpath(__DIR__ . '/../files/watermark.png'); | ||||
|         $format = new X264('aac', 'libx264'); | ||||
|         $output = __DIR__ . '/output/' . self::PATH_PREFIX . 'test_of_compatibility_with_existed_filters.mp4'; | ||||
| 
 | ||||
|         $complexMedia = $ffmpeg->openComplex($inputs); | ||||
|         $complexMedia->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]'); | ||||
|         $complexMedia | ||||
|             ->map(array('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(); | ||||
| 
 | ||||
|         $complexMedia1 = $ffmpeg->openComplex(array(__FILE__)); | ||||
|         $complexMedia1 | ||||
|             ->map(array('test'), $format, 'outputFile.mp4', false); | ||||
|         $this->assertContains('acodec', $complexMedia1->getFinalCommand()); | ||||
| 
 | ||||
|         $complexMedia2 = $ffmpeg->openComplex(array(__FILE__)); | ||||
|         $complexMedia2 | ||||
|             ->map(array('test'), $format, 'outputFile.mp4', true); | ||||
|         $this->assertNotContains('acodec', $complexMedia2->getFinalCommand()); | ||||
|     } | ||||
| 
 | ||||
|     public function testForceDisableVideo() | ||||
|     { | ||||
|         $ffmpeg = $this->getFFMpeg(); | ||||
|         $format = new X264(); | ||||
| 
 | ||||
|         $complexMedia1 = $ffmpeg->openComplex(array(__FILE__)); | ||||
|         $complexMedia1->map(array('test'), $format, | ||||
|             'outputFile.mp4', false, false); | ||||
|         $this->assertContains('vcodec', $complexMedia1->getFinalCommand()); | ||||
| 
 | ||||
|         $complexMedia2 = $ffmpeg->openComplex(array(__FILE__)); | ||||
|         $complexMedia2->map(array('test'), $format, | ||||
|             'outputFile.mp4', false, true); | ||||
|         $this->assertNotContains('vcodec', $complexMedia2->getFinalCommand()); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue