Merge branch 'master' into multipleframes
This commit is contained in:
commit
7adc8c73c0
9 changed files with 310 additions and 9 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@
|
||||||
composer.phar
|
composer.phar
|
||||||
composer.lock
|
composer.lock
|
||||||
phpunit.xml
|
phpunit.xml
|
||||||
|
sami.phar
|
||||||
|
|
|
||||||
51
README.md
51
README.md
|
|
@ -14,7 +14,7 @@ Check another amazing repo : [PHP FFMpeg extras](https://github.com/alchemy-fr/P
|
||||||
|
|
||||||
This library requires a working FFMpeg install. You will need both FFMpeg and FFProbe binaries to use it.
|
This library requires a working FFMpeg install. You will need both FFMpeg and FFProbe binaries to use it.
|
||||||
Be sure that these binaries can be located with system PATH to get the benefit of the binary detection,
|
Be sure that these binaries can be located with system PATH to get the benefit of the binary detection,
|
||||||
otherwise you should have to explicitely give the binaries path on load.
|
otherwise you should have to explicitly give the binaries path on load.
|
||||||
|
|
||||||
For Windows users : Please find the binaries at http://ffmpeg.zeranoe.com/builds/.
|
For Windows users : Please find the binaries at http://ffmpeg.zeranoe.com/builds/.
|
||||||
|
|
||||||
|
|
@ -28,12 +28,8 @@ appear in latest ffmpeg version.
|
||||||
|
|
||||||
The recommended way to install PHP-FFMpeg is through [Composer](https://getcomposer.org).
|
The recommended way to install PHP-FFMpeg is through [Composer](https://getcomposer.org).
|
||||||
|
|
||||||
```json
|
```bash
|
||||||
{
|
$ composer require php-ffmpeg/php-ffmpeg
|
||||||
"require": {
|
|
||||||
"php-ffmpeg/php-ffmpeg": "~0.5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Basic Usage
|
## Basic Usage
|
||||||
|
|
@ -69,7 +65,7 @@ $ffmpeg = FFMpeg\FFMpeg::create();
|
||||||
```
|
```
|
||||||
|
|
||||||
FFMpeg will autodetect ffmpeg and ffprobe binaries. If you want to give binary
|
FFMpeg will autodetect ffmpeg and ffprobe binaries. If you want to give binary
|
||||||
paths explicitely, you can pass an array as configuration. A `Psr\Logger\LoggerInterface`
|
paths explicitly, you can pass an array as configuration. A `Psr\Logger\LoggerInterface`
|
||||||
can also be passed to log binary executions.
|
can also be passed to log binary executions.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
@ -233,12 +229,28 @@ Resizes a video to a given size.
|
||||||
$video->filters()->resize($dimension, $mode, $useStandards);
|
$video->filters()->resize($dimension, $mode, $useStandards);
|
||||||
```
|
```
|
||||||
|
|
||||||
The resize filter takes three parameters :
|
The resize filter takes three parameters:
|
||||||
|
|
||||||
- `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`
|
- `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`
|
||||||
- `$mode`, one of the constants `FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_*` constants
|
- `$mode`, one of the constants `FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_*` constants
|
||||||
- `$useStandards`, a boolean to force the use of the nearest aspect ratio standard.
|
- `$useStandards`, a boolean to force the use of the nearest aspect ratio standard.
|
||||||
|
|
||||||
|
If you want a video in a non-standard ratio, you can use the padding filter to resize your video in the desired size, and wrap it into black bars.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$video->filters()->pad($dimension);
|
||||||
|
```
|
||||||
|
|
||||||
|
The pad filter takes one parameter:
|
||||||
|
|
||||||
|
- `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`
|
||||||
|
|
||||||
|
Don't forget to save it afterwards.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$video->save(new FFMpeg\Format\Video\X264(), $new_file);
|
||||||
|
```
|
||||||
|
|
||||||
###### Watermark
|
###### Watermark
|
||||||
|
|
||||||
Watermark a video with a given image.
|
Watermark a video with a given image.
|
||||||
|
|
@ -344,6 +356,27 @@ method. It only accepts audio filters.
|
||||||
You can build your own filters and some are bundled in PHP-FFMpeg - they are
|
You can build your own filters and some are bundled in PHP-FFMpeg - they are
|
||||||
accessible through the `FFMpeg\Media\Audio::filters` method.
|
accessible through the `FFMpeg\Media\Audio::filters` method.
|
||||||
|
|
||||||
|
###### Metadata
|
||||||
|
|
||||||
|
Add metadata to audio files. Just pass an array of key=value pairs of all
|
||||||
|
metadata you would like to add. If no arguments are passed into the filter
|
||||||
|
all metadata will be removed from input file. Currently supported data is
|
||||||
|
title, artist, album, artist, composer, track, year, description, artwork
|
||||||
|
|
||||||
|
```php
|
||||||
|
$audio->filters()->addMetadata(["title" => "Some Title", "track" => 1]);
|
||||||
|
|
||||||
|
//remove all metadata and video streams from audio file
|
||||||
|
$audio->filters()->addMetadata();
|
||||||
|
```
|
||||||
|
|
||||||
|
Add artwork to the audio file
|
||||||
|
```php
|
||||||
|
$audio->filters()->addMetadata(["artwork" => "/path/to/image/file.jpg"]);
|
||||||
|
```
|
||||||
|
NOTE: at present ffmpeg (version 3.2.2) only supports artwork output for .mp3
|
||||||
|
files
|
||||||
|
|
||||||
###### Resample
|
###### Resample
|
||||||
|
|
||||||
Resamples an audio file.
|
Resamples an audio file.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@
|
||||||
"name": "Patrik Karisch",
|
"name": "Patrik Karisch",
|
||||||
"email": "patrik@karisch.guru",
|
"email": "patrik@karisch.guru",
|
||||||
"homepage": "http://www.karisch.guru"
|
"homepage": "http://www.karisch.guru"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Romain Biard",
|
||||||
|
"email": "romain.biard@gmail.com",
|
||||||
|
"homepage": "https://www.strime.io/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
|
|
||||||
45
src/FFMpeg/Filters/Audio/AddMetadataFilter.php
Normal file
45
src/FFMpeg/Filters/Audio/AddMetadataFilter.php
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
namespace FFMpeg\Filters\Audio;
|
||||||
|
|
||||||
|
use FFMpeg\Filters\Audio\AudioFilterInterface;
|
||||||
|
use FFMpeg\Format\AudioInterface;
|
||||||
|
use FFMpeg\Media\Audio;
|
||||||
|
|
||||||
|
class AddMetadataFilter implements AudioFilterInterface
|
||||||
|
{
|
||||||
|
/** @var Array */
|
||||||
|
private $metaArr;
|
||||||
|
/** @var Integer */
|
||||||
|
private $priority;
|
||||||
|
|
||||||
|
function __construct($data = null, $priority = 9)
|
||||||
|
{
|
||||||
|
$this->metaArr = $data;
|
||||||
|
$this->priority = $priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPriority()
|
||||||
|
{
|
||||||
|
//must be of high priority in case theres a second input stream (artwork) to register with audio
|
||||||
|
return $this->priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply(Audio $audio, AudioInterface $format)
|
||||||
|
{
|
||||||
|
if (is_null($this->metaArr))
|
||||||
|
return ['-map_metadata', '-1', '-vn'];
|
||||||
|
|
||||||
|
$metadata = [];
|
||||||
|
|
||||||
|
if (array_key_exists("artwork", $this->metaArr)) {
|
||||||
|
array_push($metadata, "-i", $this->metaArr['artwork'], "-map", "0", "-map", "1");
|
||||||
|
unset($this->metaArr['artwork']);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->metaArr as $k => $v) {
|
||||||
|
array_push($metadata, "-metadata", "$k=$v");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace FFMpeg\Filters\Audio;
|
namespace FFMpeg\Filters\Audio;
|
||||||
|
|
||||||
|
use FFMpeg\Filters\Audio\AddMetadataFilter;
|
||||||
use FFMpeg\Media\Audio;
|
use FFMpeg\Media\Audio;
|
||||||
|
|
||||||
class AudioFilters
|
class AudioFilters
|
||||||
|
|
@ -26,4 +27,25 @@ class AudioFilters
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add metadata to an audio file. If no arguments are given then filter
|
||||||
|
* will remove all metadata from the audio file
|
||||||
|
* @param Array|Null $data If array must contain one of these key/value pairs:
|
||||||
|
* - "title": Title metadata
|
||||||
|
* - "artist": Artist metadata
|
||||||
|
* - "composer": Composer metadata
|
||||||
|
* - "album": Album metadata
|
||||||
|
* - "track": Track metadata
|
||||||
|
* - "artwork": Song artwork. String of file path
|
||||||
|
* - "year": Year metadata
|
||||||
|
* - "genre": Genre metadata
|
||||||
|
* - "description": Description metadata
|
||||||
|
*/
|
||||||
|
public function addMetadata($data = null)
|
||||||
|
{
|
||||||
|
$this->media->addFilter(new AddMetadataFilter($data));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
src/FFMpeg/Filters/Video/PadFilter.php
Normal file
60
src/FFMpeg/Filters/Video/PadFilter.php
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?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\Video;
|
||||||
|
|
||||||
|
use FFMpeg\Coordinate\Dimension;
|
||||||
|
use FFMpeg\Exception\RuntimeException;
|
||||||
|
use FFMpeg\Media\Video;
|
||||||
|
use FFMpeg\Format\VideoInterface;
|
||||||
|
|
||||||
|
class PadFilter implements VideoFilterInterface
|
||||||
|
{
|
||||||
|
/** @var Dimension */
|
||||||
|
private $dimension;
|
||||||
|
/** @var integer */
|
||||||
|
private $priority;
|
||||||
|
|
||||||
|
public function __construct(Dimension $dimension, $priority = 0)
|
||||||
|
{
|
||||||
|
$this->dimension = $dimension;
|
||||||
|
$this->priority = $priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPriority()
|
||||||
|
{
|
||||||
|
return $this->priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Dimension
|
||||||
|
*/
|
||||||
|
public function getDimension()
|
||||||
|
{
|
||||||
|
return $this->dimension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply(Video $video, VideoInterface $format)
|
||||||
|
{
|
||||||
|
$commands = array();
|
||||||
|
|
||||||
|
$commands[] = '-vf';
|
||||||
|
$commands[] = 'scale=iw*min(' . $this->dimension->getWidth() . '/iw\,' . $this->dimension->getHeight() .'/ih):ih*min(' . $this->dimension->getWidth() . '/iw\,' . $this->dimension->getHeight() .'/ih),pad=' . $this->dimension->getWidth() . ':' . $this->dimension->getHeight() . ':(' . $this->dimension->getWidth() . '-iw)/2:(' . $this->dimension->getHeight() .'-ih)/2';
|
||||||
|
|
||||||
|
return $commands;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -113,6 +113,20 @@ class VideoFilters extends AudioFilters
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds padding (black bars) to a video.
|
||||||
|
*
|
||||||
|
* @param Dimension $dimension
|
||||||
|
*
|
||||||
|
* @return VideoFilters
|
||||||
|
*/
|
||||||
|
public function pad(Dimension $dimension)
|
||||||
|
{
|
||||||
|
$this->media->addFilter(new PadFilter($dimension));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function rotate($angle)
|
public function rotate($angle)
|
||||||
{
|
{
|
||||||
$this->media->addFilter(new RotateFilter($angle, 30));
|
$this->media->addFilter(new RotateFilter($angle, 30));
|
||||||
|
|
@ -147,4 +161,18 @@ class VideoFilters extends AudioFilters
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a custom filter: -vf foo bar
|
||||||
|
*
|
||||||
|
* @param string $parameters
|
||||||
|
*
|
||||||
|
* @return VideoFilters
|
||||||
|
*/
|
||||||
|
public function custom($parameters)
|
||||||
|
{
|
||||||
|
$this->media->addFilter(new CustomFilter($parameters));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
63
tests/Unit/Filters/Audio/AudioMetadataTest.php
Normal file
63
tests/Unit/Filters/Audio/AudioMetadataTest.php
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?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->getMock('FFMpeg\Format\AudioInterface');
|
||||||
|
|
||||||
|
$filters = new AudioFilters($audio);
|
||||||
|
$filters->addMetadata(array('title' => "Hello World"));
|
||||||
|
$this->assertEquals(array(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->getMock('FFMpeg\Format\AudioInterface');
|
||||||
|
|
||||||
|
$filters = new AudioFilters($audio);
|
||||||
|
$filters->addMetadata(array('genre' => 'Some Genre', 'artwork' => "/path/to/file.jpg"));
|
||||||
|
$this->assertEquals(array(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->getMock('FFMpeg\Format\AudioInterface');
|
||||||
|
|
||||||
|
$filters = new AudioFilters($audio);
|
||||||
|
$filters->addMetadata();
|
||||||
|
$this->assertEquals(array(0 => "-map_metadata", 1 => "-1", 2 => "-vn"), $capturedFilter->apply($audio, $format));
|
||||||
|
}
|
||||||
|
}
|
||||||
44
tests/Unit/Filters/Video/PadFilterTest.php
Normal file
44
tests/Unit/Filters/Video/PadFilterTest.php
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||||
|
|
||||||
|
use FFMpeg\Filters\Video\PadFilter;
|
||||||
|
use Tests\FFMpeg\Unit\TestCase;
|
||||||
|
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||||
|
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||||
|
use FFMpeg\Coordinate\Dimension;
|
||||||
|
|
||||||
|
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->getMock('FFMpeg\Format\VideoInterface');
|
||||||
|
|
||||||
|
$streams = new StreamCollection(array(
|
||||||
|
new Stream(array(
|
||||||
|
'codec_type' => 'video',
|
||||||
|
'width' => $width,
|
||||||
|
'height' => $height,
|
||||||
|
))
|
||||||
|
));
|
||||||
|
|
||||||
|
$filter = new PadFilter($dimension);
|
||||||
|
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideDimensions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(new Dimension(1000, 800), 640, 480, array('-vf', 'scale=iw*min(1000/iw\,800/ih):ih*min(1000/iw\,800/ih),pad=1000:800:(1000-iw)/2:(800-ih)/2')),
|
||||||
|
array(new Dimension(300, 600), 640, 480, array('-vf', 'scale=iw*min(300/iw\,600/ih):ih*min(300/iw\,600/ih),pad=300:600:(300-iw)/2:(600-ih)/2')),
|
||||||
|
array(new Dimension(100, 900), 640, 480, array('-vf', 'scale=iw*min(100/iw\,900/ih):ih*min(100/iw\,900/ih),pad=100:900:(100-iw)/2:(900-ih)/2')),
|
||||||
|
array(new Dimension(1200, 200), 640, 480, array('-vf', 'scale=iw*min(1200/iw\,200/ih):ih*min(1200/iw\,200/ih),pad=1200:200:(1200-iw)/2:(200-ih)/2')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue