Version 0.3

This commit is contained in:
Romain Neutron 2013-06-25 10:03:20 +02:00
commit ad3a5af623
130 changed files with 7283 additions and 2627 deletions

View file

@ -0,0 +1,80 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe\DataMapping;
use FFMpeg\Exception\InvalidArgumentException;
abstract class AbstractData implements \Countable
{
private $properties;
public function __construct(array $properties)
{
$this->properties = $properties;
}
/**
* Returns true if data has property
*
* @param string $property
* @return Boolean
*/
public function has($property)
{
return isset($this->properties[$property]);
}
/**
* Returns the property value given its name
*
* @param string $property
* @return mixed
*
* @throws InvalidArgumentException In case the data does not have the property
*/
public function get($property)
{
if (!isset($this->properties[$property])) {
throw new InvalidArgumentException(sprintf('Invalid property `%s`.', $property));
}
return $this->properties[$property];
}
/**
* Returns all property names
*
* @return array
*/
public function keys()
{
return array_keys($this->properties);
}
/**
* Returns all properties and their values
*
* @return array
*/
public function all()
{
return $this->properties;
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->properties);
}
}

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe\DataMapping;
class Format extends AbstractData
{
}

View file

@ -0,0 +1,35 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe\DataMapping;
class Stream extends AbstractData
{
/**
* Returns true if the stream is an audio stream
*
* @return Boolean
*/
public function isAudio()
{
return $this->has('codec_type') ? 'audio' === $this->get('codec_type') : false;
}
/**
* Returns true if the stream is a video stream
*
* @return Boolean
*/
public function isVideo()
{
return $this->has('codec_type') ? 'video' === $this->get('codec_type') : false;
}
}

View file

@ -0,0 +1,99 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe\DataMapping;
class StreamCollection implements \Countable, \IteratorAggregate
{
private $streams;
public function __construct(array $streams = array())
{
$this->streams = array_values($streams);
}
/**
* Returns the first stream of the collection, null if the collection is
* empty.
*
* @return null|Stream
*/
public function first()
{
$stream = reset($this->streams);
return $stream ?: null;
}
/**
* Adds a stream to the collection
*
* @param Stream $stream
*
* @return StreamCollection
*/
public function add(Stream $stream)
{
$this->streams[] = $stream;
return $this;
}
/**
* Returns a new StreamCollection with only video streams
*
* @return StreamCollection
*/
public function videos()
{
return new static(array_filter($this->streams, function (Stream $stream) {
return $stream->isVideo();
}));
}
/**
* Returns a new StreamCollection with only audio streams
*
* @return StreamCollection
*/
public function audios()
{
return new static(array_filter($this->streams, function (Stream $stream) {
return $stream->isAudio();
}));
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->streams);
}
/**
* Returns the array of contained streams
*
* @return array
*/
public function all()
{
return $this->streams;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new \ArrayIterator($this->streams);
}
}

View file

@ -0,0 +1,54 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe;
use FFMpeg\FFProbe;
use FFMpeg\FFProbe\DataMapping\Format;
use FFMpeg\FFProbe\DataMapping\StreamCollection;
use FFMpeg\FFProbe\DataMapping\Stream;
use FFMpeg\Exception\InvalidArgumentException;
class Mapper implements MapperInterface
{
/**
* {@inheritdoc}
*/
public function map($type, $data)
{
switch ($type) {
case FFProbe::TYPE_FORMAT:
return $this->mapFormat($data);
case FFProbe::TYPE_STREAMS:
return $this->mapStreams($data);
default:
throw new InvalidArgumentException(sprintf(
'Invalid type `%s`.', $type
));
}
}
private function mapFormat($data)
{
return new Format($data['format']);
}
private function mapStreams($data)
{
$streams = new StreamCollection();
foreach ($data['streams'] as $properties) {
$streams->add(new Stream($properties));
}
return $streams;
}
}

View file

@ -0,0 +1,27 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe;
interface MapperInterface
{
/**
* Maps data given its type
*
* @param string $type One of FFProbe::TYPE_* constant
* @param string $data The data
*
* @return Format|Stream
*
* @throws InvalidArgumentException In case the type is not supported
*/
public function map($type, $data);
}

View file

@ -0,0 +1,64 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe;
use Doctrine\Common\Cache\Cache;
use FFMpeg\Driver\FFProbeDriver;
class OptionsTester implements OptionsTesterInterface
{
/** @var FFProbeDriver */
private $ffprobe;
/** @var Cache */
private $cache;
public function __construct(FFProbeDriver $ffprobe, Cache $cache)
{
$this->ffprobe = $ffprobe;
$this->cache = $cache;
}
/**
* {@inheritdoc}
*/
public function has($name)
{
$id = sprintf('option-%s', $name);
if ($this->cache->contains($id)) {
return $this->cache->fetch($id);
}
$output = $this->retrieveHelpOutput();
$ret = (Boolean) preg_match('/^'.$name.'/m', $output);
$this->cache->save($id, $ret);
return $ret;
}
private function retrieveHelpOutput()
{
$id = 'help';
if ($this->cache->contains($id)) {
return $this->cache->fetch($id);
}
$output = $this->ffprobe->command(array('-help', '-loglevel', 'quiet'));
$this->cache->save($id, $output);
return $output;
}
}

View file

@ -0,0 +1,24 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe;
interface OptionsTesterInterface
{
/**
* Tells if the given option is supported by ffprobe
*
* @param string $name
*
* @return Boolean
*/
public function has($name);
}

View file

@ -0,0 +1,125 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe;
use FFMpeg\FFProbe;
use FFMpeg\Exception\InvalidArgumentException;
class OutputParser implements OutputParserInterface
{
/**
* {@inheritdoc}
*/
public function parse($type, $data)
{
switch ($type) {
case FFProbe::TYPE_FORMAT:
return $this->parseFormat($data);
break;
case FFProbe::TYPE_STREAMS:
return $this->parseStreams($data);
break;
default:
throw new InvalidArgumentException(sprintf('Unknown data type %s', $type));
}
}
private function parseFormat($data)
{
$ret = array();
foreach (explode(PHP_EOL, $data) as $line) {
if (in_array($line, array('[FORMAT]', '[/FORMAT]'))) {
continue;
}
$chunks = explode('=', $line);
$key = array_shift($chunks);
if ('' === trim($key)) {
continue;
}
$value = trim(implode('=', $chunks));
if ('nb_streams' === $key) {
$value = (int) $value;
}
if (0 === strpos($key, 'TAG:')) {
if (!isset($ret['tags'])) {
$ret['tags'] = array();
}
$ret['tags'][substr($key, 4)] = $value;
} else {
$ret[$key] = $value;
}
}
return array('format' => $ret);
}
private function parseStreams($data)
{
$ret = array();
$n = -1;
foreach (explode(PHP_EOL, $data) as $line) {
if ($line == '[STREAM]') {
$n ++;
$ret[$n] = array();
continue;
}
if ($line == '[/STREAM]') {
continue;
}
$chunks = explode('=', $line);
$key = array_shift($chunks);
if ('' === trim($key)) {
continue;
}
$value = trim(implode('=', $chunks));
if ('N/A' === $value) {
continue;
}
if ('profile' === $key && 'unknown' === $value) {
continue;
}
if (in_array($key, array('index', 'width', 'height', 'channels', 'bits_per_sample', 'has_b_frames', 'level', 'start_pts', 'duration_ts'))) {
$value = (int) $value;
}
if (0 === strpos($key, 'TAG:')) {
if (!isset($ret[$n]['tags'])) {
$ret[$n]['tags'] = array();
}
$ret[$n]['tags'][substr($key, 4)] = $value;
} elseif (0 === strpos($key, 'DISPOSITION:')) {
if (!isset($ret[$n]['disposition'])) {
$ret[$n]['disposition'] = array();
}
$ret[$n]['disposition'][substr($key, 12)] = $value;
} else {
$ret[$n][$key] = $value;
}
}
return array('streams' => $ret);
}
}

View file

@ -0,0 +1,27 @@
<?php
/*
* This file is part of PHP-FFmpeg.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FFMpeg\FFProbe;
interface OutputParserInterface
{
/**
* Parses ffprobe raw output
*
* @param string $type One of FFProbe::TYPE_* constant
* @param string $data The data
*
* @return array
*
* @throws InvalidArgumentException In case the type is not supported
*/
public function parse($type, $data);
}