Concatenation feature (#287)
* Creation of a feature to concatenate files into a new one. * Update of the README * Creation of the tests for the concatenation * We use an array of videos instead of a path to a text files * We use the bundle Temporary File System instead of getcwd
This commit is contained in:
parent
a6f6bbcb1e
commit
21c28dea25
8 changed files with 541 additions and 1 deletions
58
README.md
58
README.md
|
|
@ -405,7 +405,7 @@ accurate images ; it takes more time to execute.
|
||||||
|
|
||||||
#### Gif
|
#### Gif
|
||||||
|
|
||||||
A gif is an animated image extracted from a sequence of the video ;.
|
A gif is an animated image extracted from a sequence of the video.
|
||||||
|
|
||||||
You can save gif files using the `FFMpeg\Media\Gif::save` method.
|
You can save gif files using the `FFMpeg\Media\Gif::save` method.
|
||||||
|
|
||||||
|
|
@ -419,6 +419,62 @@ $video
|
||||||
This method has a third optional boolean parameter, which is the duration of the animation.
|
This method has a third optional boolean parameter, which is the duration of the animation.
|
||||||
If you don't set it, you will get a fixed gif image.
|
If you don't set it, you will get a fixed gif image.
|
||||||
|
|
||||||
|
#### Concatenation
|
||||||
|
|
||||||
|
This feature allows you to generate one audio or video file, based on multiple sources.
|
||||||
|
|
||||||
|
There are two ways to concatenate videos, depending on the codecs of the sources.
|
||||||
|
If your sources have all been encoded with the same codec, you will want to use the `FFMpeg\Media\Concatenate::saveFromSameCodecs` which has way better performances.
|
||||||
|
If your sources have been encoded with different codecs, you will want to use the `FFMpeg\Media\Concatenate::saveFromDifferentCodecs`.
|
||||||
|
|
||||||
|
The first function will use the initial codec as the one for the generated file.
|
||||||
|
With the second function, you will be able to choose which codec you want for the generated file.
|
||||||
|
|
||||||
|
You also need to pay attention to the fact that, when using the saveFromDifferentCodecs method,
|
||||||
|
your files MUST have video and audio streams.
|
||||||
|
|
||||||
|
In both cases, you will have to provide a list of files in a TXT file.
|
||||||
|
The TXT file will one path per line. Here is an example:
|
||||||
|
|
||||||
|
`txt
|
||||||
|
file './concat-1.mp4'
|
||||||
|
file 'concat-2.mp4'
|
||||||
|
#file 'concat-3.mp4'
|
||||||
|
`
|
||||||
|
|
||||||
|
In this example, the third file will be ignored.
|
||||||
|
Please refer to the [documentation](https://trac.ffmpeg.org/wiki/Concatenate) for more details.
|
||||||
|
|
||||||
|
To concatenate videos encoded with the same codec, do as follow:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// In order to instantiate the video object, you HAVE TO pass a path to a valid video file.
|
||||||
|
// We recommand that you put there the path of any of the video you want to use in this concatenation.
|
||||||
|
$video = $ffmpeg->open( '/path/to/video' );
|
||||||
|
$video
|
||||||
|
->concat('/path/to/list.txt')
|
||||||
|
->saveFromSameCodecs('/path/to/new_file', TRUE);
|
||||||
|
```
|
||||||
|
|
||||||
|
The boolean parameter of the save function allows you to use the copy parameter which accelerates drastically the generation of the encoded file.
|
||||||
|
|
||||||
|
To concatenate videos encoded with the same codec, do as follow:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// In order to instantiate the video object, you HAVE TO pass a path to a valid video file.
|
||||||
|
// We recommand that you put there the path of any of the video you want to use in this concatenation.
|
||||||
|
$video = $ffmpeg->open( '/path/to/video' );
|
||||||
|
|
||||||
|
$format = new FFMpeg\Format\Video\X264();
|
||||||
|
$format->setAudioCodec("libmp3lame");
|
||||||
|
|
||||||
|
$video
|
||||||
|
->concat('/path/to/list.txt')
|
||||||
|
->saveFromDifferentCodecs($format, '/path/to/new_file');
|
||||||
|
```
|
||||||
|
|
||||||
|
More details about concatenation in FFMPEG can be found [here](https://trac.ffmpeg.org/wiki/Concatenate), [here](https://ffmpeg.org/ffmpeg-formats.html#concat-1) and [here](https://ffmpeg.org/ffmpeg.html#Stream-copy).
|
||||||
|
|
||||||
#### Formats
|
#### Formats
|
||||||
|
|
||||||
A format implements `FFMpeg\Format\FormatInterface`. To save to a video file,
|
A format implements `FFMpeg\Format\FormatInterface`. To save to a video file,
|
||||||
|
|
|
||||||
20
src/FFMpeg/Filters/Concat/ConcatFilterInterface.php
Normal file
20
src/FFMpeg/Filters/Concat/ConcatFilterInterface.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PHP-FFmpeg.
|
||||||
|
*
|
||||||
|
* (c) Strime <contact@strime.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FFMpeg\Filters\Concat;
|
||||||
|
|
||||||
|
use FFMpeg\Filters\FilterInterface;
|
||||||
|
use FFMpeg\Media\Concat;
|
||||||
|
|
||||||
|
interface ConcatFilterInterface extends FilterInterface
|
||||||
|
{
|
||||||
|
public function apply(Concat $concat);
|
||||||
|
}
|
||||||
24
src/FFMpeg/Filters/Concat/ConcatFilters.php
Normal file
24
src/FFMpeg/Filters/Concat/ConcatFilters.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PHP-FFmpeg.
|
||||||
|
*
|
||||||
|
* (c) Strime <contact@strime.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FFMpeg\Filters\Concat;
|
||||||
|
|
||||||
|
use FFMpeg\Media\Concat;
|
||||||
|
|
||||||
|
class ConcatFilters
|
||||||
|
{
|
||||||
|
private $concat;
|
||||||
|
|
||||||
|
public function __construct(Concat $concat)
|
||||||
|
{
|
||||||
|
$this->concat = $concat;
|
||||||
|
}
|
||||||
|
}
|
||||||
259
src/FFMpeg/Media/Concat.php
Normal file
259
src/FFMpeg/Media/Concat.php
Normal file
|
|
@ -0,0 +1,259 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PHP-FFmpeg.
|
||||||
|
*
|
||||||
|
* (c) Strime <contact@strime.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FFMpeg\Media;
|
||||||
|
|
||||||
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
|
||||||
|
use Alchemy\BinaryDriver\Exception\InvalidArgumentException;
|
||||||
|
use FFMpeg\Filters\Concat\ConcatFilterInterface;
|
||||||
|
use FFMpeg\Filters\Concat\ConcatFilters;
|
||||||
|
use FFMpeg\Driver\FFMpegDriver;
|
||||||
|
use FFMpeg\FFProbe;
|
||||||
|
use FFMpeg\Filters\Audio\SimpleFilter;
|
||||||
|
use FFMpeg\Exception\RuntimeException;
|
||||||
|
use FFMpeg\Format\FormatInterface;
|
||||||
|
use FFMpeg\Filters\FilterInterface;
|
||||||
|
use FFMpeg\Format\ProgressableInterface;
|
||||||
|
use FFMpeg\Format\AudioInterface;
|
||||||
|
use FFMpeg\Format\VideoInterface;
|
||||||
|
use Neutron\TemporaryFilesystem\Manager as FsManager;
|
||||||
|
|
||||||
|
class Concat extends AbstractMediaType
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private $sources;
|
||||||
|
|
||||||
|
public function __construct($sources, FFMpegDriver $driver, FFProbe $ffprobe)
|
||||||
|
{
|
||||||
|
parent::__construct($sources, $driver, $ffprobe);
|
||||||
|
$this->sources = $sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path to the sources.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSources()
|
||||||
|
{
|
||||||
|
return $this->sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return ConcatFilters
|
||||||
|
*/
|
||||||
|
public function filters()
|
||||||
|
{
|
||||||
|
return new ConcatFilters($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return Concat
|
||||||
|
*/
|
||||||
|
public function addFilter(ConcatFilterInterface $filter)
|
||||||
|
{
|
||||||
|
$this->filters->add($filter);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the concatenated video in the given array, considering that the sources videos are all encoded with the same codec.
|
||||||
|
*
|
||||||
|
* @param array $outputPathfile
|
||||||
|
* @param string $streamCopy
|
||||||
|
*
|
||||||
|
* @return Concat
|
||||||
|
*
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function saveFromSameCodecs($outputPathfile, $streamCopy = TRUE)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @see https://ffmpeg.org/ffmpeg-formats.html#concat
|
||||||
|
* @see https://trac.ffmpeg.org/wiki/Concatenate
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create the file which will contain the list of videos
|
||||||
|
$fs = FsManager::create();
|
||||||
|
$sourcesFile = $fs->createTemporaryFile('ffmpeg-concat');
|
||||||
|
|
||||||
|
// Set the content of this file
|
||||||
|
$fileStream = fopen($sourcesFile, 'w') or die("Cannot open file.");
|
||||||
|
$count_videos = 0;
|
||||||
|
if(is_array($this->sources) && (count($this->sources) > 0)) {
|
||||||
|
foreach ($this->sources as $videoPath) {
|
||||||
|
$line = "";
|
||||||
|
|
||||||
|
if($count_videos != 0)
|
||||||
|
$line .= "\n";
|
||||||
|
|
||||||
|
$line .= "file ".$videoPath;
|
||||||
|
|
||||||
|
fwrite($fileStream, $line);
|
||||||
|
|
||||||
|
$count_videos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new InvalidArgumentException('The list of videos is not a valid array.');
|
||||||
|
}
|
||||||
|
fclose($fileStream);
|
||||||
|
|
||||||
|
|
||||||
|
$commands = array(
|
||||||
|
'-f', 'concat', '-safe', '0',
|
||||||
|
'-i', $sourcesFile
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if stream copy is activated
|
||||||
|
if($streamCopy === TRUE) {
|
||||||
|
$commands[] = '-c';
|
||||||
|
$commands[] = 'copy';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not, we can apply filters
|
||||||
|
else {
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
$commands = array_merge($commands, $filter->apply($this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the output file in the command
|
||||||
|
$commands = array_merge($commands, array($outputPathfile));
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
try {
|
||||||
|
$this->driver->command($commands);
|
||||||
|
} catch (ExecutionFailureException $e) {
|
||||||
|
$this->cleanupTemporaryFile($outputPathfile);
|
||||||
|
$this->cleanupTemporaryFile($sourcesFile);
|
||||||
|
throw new RuntimeException('Unable to save concatenated video', $e->getCode(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cleanupTemporaryFile($sourcesFile);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the concatenated video in the given filename, considering that the sources videos are all encoded with the same codec.
|
||||||
|
*
|
||||||
|
* @param string $outputPathfile
|
||||||
|
*
|
||||||
|
* @return Concat
|
||||||
|
*
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function saveFromDifferentCodecs(FormatInterface $format, $outputPathfile)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @see https://ffmpeg.org/ffmpeg-formats.html#concat
|
||||||
|
* @see https://trac.ffmpeg.org/wiki/Concatenate
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check the validity of the parameter
|
||||||
|
if(!is_array($this->sources) || (count($this->sources) == 0)) {
|
||||||
|
throw new InvalidArgumentException('The list of videos is not a valid array.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the commands variable
|
||||||
|
$commands = array();
|
||||||
|
|
||||||
|
// Prepare the parameters
|
||||||
|
$nbSources = 0;
|
||||||
|
$files = array();
|
||||||
|
|
||||||
|
// For each source, check if this is a legit file
|
||||||
|
// and prepare the parameters
|
||||||
|
foreach ($this->sources as $videoPath) {
|
||||||
|
$files[] = '-i';
|
||||||
|
$files[] = $videoPath;
|
||||||
|
$nbSources++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commands = array_merge($commands, $files);
|
||||||
|
|
||||||
|
// Set the parameters of the request
|
||||||
|
$commands[] = '-filter_complex';
|
||||||
|
|
||||||
|
$complex_filter = '';
|
||||||
|
for($i=0; $i<$nbSources; $i++) {
|
||||||
|
$complex_filter .= '['.$i.':v:0] ['.$i.':a:0] ';
|
||||||
|
}
|
||||||
|
$complex_filter .= 'concat=n='.$nbSources.':v=1:a=1 [v] [a]';
|
||||||
|
|
||||||
|
$commands[] = $complex_filter;
|
||||||
|
$commands[] = '-map';
|
||||||
|
$commands[] = '[v]';
|
||||||
|
$commands[] = '-map';
|
||||||
|
$commands[] = '[a]';
|
||||||
|
|
||||||
|
// Prepare the filters
|
||||||
|
$filters = clone $this->filters;
|
||||||
|
$filters->add(new SimpleFilter($format->getExtraParams(), 10));
|
||||||
|
|
||||||
|
if ($this->driver->getConfiguration()->has('ffmpeg.threads')) {
|
||||||
|
$filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads'))));
|
||||||
|
}
|
||||||
|
if ($format instanceof VideoInterface) {
|
||||||
|
if (null !== $format->getVideoCodec()) {
|
||||||
|
$filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($format instanceof AudioInterface) {
|
||||||
|
if (null !== $format->getAudioCodec()) {
|
||||||
|
$filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the filters
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
$commands = array_merge($commands, $filter->apply($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($format instanceof AudioInterface) {
|
||||||
|
if (null !== $format->getAudioKiloBitrate()) {
|
||||||
|
$commands[] = '-b:a';
|
||||||
|
$commands[] = $format->getAudioKiloBitrate() . 'k';
|
||||||
|
}
|
||||||
|
if (null !== $format->getAudioChannels()) {
|
||||||
|
$commands[] = '-ac';
|
||||||
|
$commands[] = $format->getAudioChannels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user passed some additional parameters
|
||||||
|
if ($format instanceof VideoInterface) {
|
||||||
|
if (null !== $format->getAdditionalParameters()) {
|
||||||
|
foreach ($format->getAdditionalParameters() as $additionalParameter) {
|
||||||
|
$commands[] = $additionalParameter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the output file in the command
|
||||||
|
$commands[] = $outputPathfile;
|
||||||
|
|
||||||
|
$failure = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->driver->command($commands);
|
||||||
|
} catch (ExecutionFailureException $e) {
|
||||||
|
throw new RuntimeException('Encoding failed', $e->getCode(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -204,4 +204,15 @@ class Video extends Audio
|
||||||
{
|
{
|
||||||
return new Gif($this, $this->driver, $this->ffprobe, $at, $dimension, $duration);
|
return new Gif($this, $this->driver, $this->ffprobe, $at, $dimension, $duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates a list of videos into one unique video.
|
||||||
|
*
|
||||||
|
* @param string $sources
|
||||||
|
* @return Concat
|
||||||
|
*/
|
||||||
|
public function concat($sources)
|
||||||
|
{
|
||||||
|
return new Concat($sources, $this->driver, $this->ffprobe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
147
tests/Unit/Media/ConcatTest.php
Normal file
147
tests/Unit/Media/ConcatTest.php
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\FFMpeg\Unit\Media;
|
||||||
|
|
||||||
|
use FFMpeg\Media\Concat;
|
||||||
|
use Neutron\TemporaryFilesystem\Manager as FsManager;
|
||||||
|
|
||||||
|
class ConcatTest extends AbstractMediaTestCase
|
||||||
|
{
|
||||||
|
public function testGetSources()
|
||||||
|
{
|
||||||
|
$driver = $this->getFFMpegDriverMock();
|
||||||
|
$ffprobe = $this->getFFProbeMock();
|
||||||
|
|
||||||
|
$concat = new Concat(array(__FILE__, __FILE__), $driver, $ffprobe);
|
||||||
|
$this->assertSame(array(__FILE__, __FILE__), $concat->getSources());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFiltersReturnFilters()
|
||||||
|
{
|
||||||
|
$driver = $this->getFFMpegDriverMock();
|
||||||
|
$ffprobe = $this->getFFProbeMock();
|
||||||
|
|
||||||
|
$concat = new Concat(array(__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->getMock('FFMpeg\Filters\Concat\ConcatFilterInterface');
|
||||||
|
|
||||||
|
$filters->expects($this->once())
|
||||||
|
->method('add')
|
||||||
|
->with($filter);
|
||||||
|
|
||||||
|
$concat = new Concat(array(__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(array(__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]) && (strcmp($commands[6], "-c") == 0)) {
|
||||||
|
$this->assertEquals('-c', $commands[6]);
|
||||||
|
$this->assertEquals('copy', $commands[7]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSaveFromSameCodecsOptions()
|
||||||
|
{
|
||||||
|
$fs = FsManager::create();
|
||||||
|
$tmpFile = $fs->createTemporaryFile('ffmpeg-concat');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
TRUE,
|
||||||
|
array(
|
||||||
|
'-f', 'concat',
|
||||||
|
'-safe', '0',
|
||||||
|
'-i', $tmpFile,
|
||||||
|
'-c', 'copy'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
FALSE,
|
||||||
|
array(
|
||||||
|
'-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->getMock('Alchemy\BinaryDriver\ConfigurationInterface');
|
||||||
|
|
||||||
|
$driver->expects($this->any())
|
||||||
|
->method('getConfiguration')
|
||||||
|
->will($this->returnValue($configuration));
|
||||||
|
|
||||||
|
$driver->expects($this->once())
|
||||||
|
->method('command')
|
||||||
|
->with($commands);
|
||||||
|
|
||||||
|
$concat = new Concat(array(__FILE__, 'concat-2.mp4'), $driver, $ffprobe);
|
||||||
|
$this->assertSame($concat, $concat->saveFromDifferentCodecs($format, $pathfile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSaveFromDifferentCodecsOptions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'-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]'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -148,4 +148,24 @@ class TestCase extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
return $video;
|
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(array()));
|
||||||
|
|
||||||
|
return $FormatInterface;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
tests/files/concat-list.txt
Normal file
3
tests/files/concat-list.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
file './concat-1.mp4'
|
||||||
|
file 'concat-2.mp4'
|
||||||
|
#file 'concat-3.mp4'
|
||||||
Loading…
Add table
Add a link
Reference in a new issue