<?php
namespace FFMpegPush;
use Evenement\EventEmitter;
use FFMpegPush\Command\FFProbeCommand;
use FFMpegPush\Exception\RuntimeException;
use FFMpegPush\Listeners\ListenerInterface;
class PushProgressListener extends EventEmitter implements ListenerInterface
{
private $duration;
private $totalSize;
private $currentSize;
private $currentTime;
private $lastOutput = null;
private $ffprobe;
private $pathfile;
private $initialized = false;
private $rate;
private $percent = 0;
private $remaining = null;
public function setPathfile($pathfile)
{
$this->pathfile = $pathfile;
return $this;
}
public function __construct(FFProbeCommand $ffprobe, $pathfile)
{
$this->ffprobe = $ffprobe;
$this->pathfile = $pathfile;
}
public function getFFProbe()
{
return $this->ffprobe;
}
public function getPathfile()
{
return $this->pathfile;
}
public function getCurrentTime()
{
return $this->currentTime;
}
public function handle($type, $data)
{
if (null !== $progress = $this->parseProgress($data)) {
$this->emit('progress', array_values($progress));
}
}
protected function getPattern()
{
return '/size=(.*?) time=(.*?) /';
}
private function parseProgress($progress)
{
if (!$this->initialized) {
$this->initialize();
}
if (null === $this->totalSize || null === $this->duration) {
return;
}
$matches = array();
if (preg_match($this->getPattern(), $progress, $matches) !== 1) {
return null;
}
$currentDuration = $this->convertDuration($matches[2]);
$currentTime = microtime(true);
$currentSize = trim(str_replace('kb', '', strtolower(($matches[1]))));
$percent = max(0, min(1, $currentDuration / $this->duration));
if ($this->lastOutput !== null) {
$delta = $currentTime - $this->lastOutput;
if (!is_numeric($currentSize)) {
$currentSize = (int)$currentSize;
}
$deltaSize = $currentSize - $this->currentSize;
$rate = $deltaSize * $delta;
if ($rate > 0) {
$totalDuration = $this->totalSize / $rate;
$this->remaining = floor($totalDuration - ($totalDuration * $percent));
$this->rate = floor($rate);
} else {
$this->remaining = 0;
$this->rate = 0;
}
}
$this->percent = floor($percent * 100);
$this->lastOutput = $currentTime;
$this->currentSize = (int)$currentSize;
$this->currentTime = $currentDuration;
return $this->getProgressInfo();
}
private function convertDuration($rawDuration)
{
$ar = array_reverse(explode(":", $rawDuration));
$duration = floatval($ar[0]);
if (!empty($ar[1])) {
$duration += intval($ar[1]) * 60;
}
if (!empty($ar[2])) {
$duration += intval($ar[2]) * 60 * 60;
}
return $duration;
}
private function getProgressInfo()
{
if ($this->remaining === null) {
return null;
}
return array(
'percent' => $this->percent,
'remaining' => $this->remaining,
'rate' => $this->rate,
'pushInfo' => $this->getPushInfo()
);
}
private function initialize()
{
try {
$format = $this->ffprobe->format($this->pathfile);
} catch (RuntimeException $e) {
return;
}
if (false === $format->has('size') || false === $format->has('duration')) {
return;
}
$this->totalSize = $format->get('size') / 1024;
$this->duration = $format->get('duration');
$this->initialized = true;
}
public function getPushInfo()
{
return PushInfo::create()
->setCurrentSize($this->currentSize)
->setCurrentTime($this->currentTime)
->setDuration($this->duration)
->setPathfile($this->pathfile)
->setRate($this->rate)
->setRemaining($this->remaining)
->setPercent($this->percent)
->setTotalSize($this->totalSize);
}
public static function create(FFProbeCommand $ffprobe, $pathfile = "")
{
return new static($ffprobe, $pathfile);
}
}