Merge branch 'master' into multipleframes

This commit is contained in:
Romain Biard 2017-01-13 12:28:36 -03:00 committed by GitHub
commit 7adc8c73c0
9 changed files with 310 additions and 9 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@
composer.phar
composer.lock
phpunit.xml
sami.phar

View file

@ -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.
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/.
@ -28,12 +28,8 @@ appear in latest ffmpeg version.
The recommended way to install PHP-FFMpeg is through [Composer](https://getcomposer.org).
```json
{
"require": {
"php-ffmpeg/php-ffmpeg": "~0.5"
}
}
```bash
$ composer require php-ffmpeg/php-ffmpeg
```
## Basic Usage
@ -69,7 +65,7 @@ $ffmpeg = FFMpeg\FFMpeg::create();
```
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.
```php
@ -233,12 +229,28 @@ Resizes a video to a given size.
$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`
- `$mode`, one of the constants `FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_*` constants
- `$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 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
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
Resamples an audio file.

View file

@ -19,6 +19,11 @@
"name": "Patrik Karisch",
"email": "patrik@karisch.guru",
"homepage": "http://www.karisch.guru"
},
{
"name": "Romain Biard",
"email": "romain.biard@gmail.com",
"homepage": "https://www.strime.io/"
}
],
"require": {

View 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;
}
}

View file

@ -2,6 +2,7 @@
namespace FFMpeg\Filters\Audio;
use FFMpeg\Filters\Audio\AddMetadataFilter;
use FFMpeg\Media\Audio;
class AudioFilters
@ -26,4 +27,25 @@ class AudioFilters
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;
}
}

View 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;
}
}

View file

@ -113,6 +113,20 @@ class VideoFilters extends AudioFilters
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)
{
$this->media->addFilter(new RotateFilter($angle, 30));
@ -147,4 +161,18 @@ class VideoFilters extends AudioFilters
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;
}
}

View 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));
}
}

View 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')),
);
}
}