PHP Classes

File: src/PHPVideoToolkit/FfmpegProcess.php

Recommend this page to a friend!
  Classes of Oliver Lillie   PHP Video Toolkit   src/PHPVideoToolkit/FfmpegProcess.php   Download  
File: src/PHPVideoToolkit/FfmpegProcess.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP Video Toolkit
Manipulate and convert videos with ffmpeg program
Author: By
Last change: Some Fixes
- Corrected some PHPDoc for IDE Recommendation
- added IntelliJ folder to ignore
#40 add remove command functions to remove commands from the process

Signed-off-by: Oliver Lillie <buggedcom@gmail.com>
updated exception types thrown

Signed-off-by: Oliver Lillie <buggedcom@gmail.com>
updated class documentation. Added various exception changes where required.
FfmpegProcess::getProcessId new throws an exception if it is called from anywhere other than PHPVideoToolkit\Media.
fixed issues with portable progress handler
amended native progress handler to also include input and output file information
updated multiple output code to work with new renaming system
fixed bug in multiple output getCommand where $index is not supplied to the function
Merge branch 'refs/heads/multiple-output'

Conflicts:
README.md
src/PHPVideoToolkit/ProgressHandlerOutput.php
src/PHPVideoToolkit/ProgressHandlerPortable.php
updated version in source
amends for a previous commit where the garbage collection if using a portable id for the portable progress handler would not be turned off
Date: 1 year ago
Size: 30,015 bytes
 

Contents

Class file image Download
<?php /** * This file is part of the PHP Video Toolkit v2 package. * * @author Oliver Lillie (aka buggedcom) <publicmail@buggedcom.co.uk> * @license Dual licensed under MIT and GPLv2 * @copyright Copyright (c) 2008-2014 Oliver Lillie <http://www.buggedcom.co.uk> * @package PHPVideoToolkit V2 * @version 2.1.7-beta * @uses ffmpeg http://ffmpeg.sourceforge.net/ */ namespace PHPVideoToolkit; /** * This class is the base class for creating a specific ffmpeg command call. * * @author Oliver Lillie */ class FfmpegProcess extends ProcessBuilder { /** * Variable placeholder for containing the ExecBuffer object. * @access protected * @var ExecBuffer */ protected $_exec; /** * An array of commands to give to ffmpeg befor the -i input command. * @access protected * @var array */ protected $_pre_input_commands; /** * An array of input media. * @access protected * @var array */ protected $_input; /** * Variable placeholder for the current output index. * @access protected * @var integer */ protected $_output_index; /** * An array of commands to give to ffmpeg after the input -i commands are given. * @access protected * @var array */ protected $_post_input_commands; /** * An array of output paths. * @access protected * @var array */ protected $_output; /** * An array of commands to give to ffmpeg after the output is given. * @access protected * @var array */ protected $_post_output_commands; /** * Variable placeholder for the progress handler, if any, that is attached to the process. * @access protected * @var ProgressHandlerAbstract */ protected $_progress_handler; /** * Variable placeholder for a boolean value that determins if the commands supplied to this object have already * been combined together into a command string. * @access protected * @var boolean */ protected $_combined; /** * Constructor * * @access public * @author Oliver Lillie * @param string $program The programme to call. Note this is not the path. If you wish to call ffmpeg/aconv you should jsut * supply 'ffmpeg' and then set the aconv path as the ffmpeg configuration option in Config. * @param Config $config The config object. */ public function __construct($program, Config $config=null) { parent::__construct($program, $config); $this->_pre_input_commands = array(); $this->_input = array(); $this->_output_index = 0; $this->_post_input_commands = array(); $this->_output = array(); $this->_post_output_commands = array(); $this->_exec = null; $this->_progress_handler = null; $this->_combined = false; } /** * Sets the output index to a specific index. * * @access public * @author Oliver Lillie * @param integer $index The index integer to set the output index to. * @return FfmpegProcess Returns the current object. * @throws \InvalidArgumentException If the $index is not an integer. */ public function setOutputIndex($index) { if(is_int($index) === false) { throw new \InvalidArgumentException('The output index must be an integer.'); } $this->_output_index = $index; return $this; } /** * Sets the input at the given index. If -1 is used, the input is shifted * onto the begining of the input array. * * @access public * @author Oliver Lillie * @param string $input The string file path to the input media. * @param integer $index The index to which the input is being added. If null then the input is just appended to the * list of input media. If a positive index then it is set at the given index, it will overwrite anything already in that * position. If -1 then the input is shifted onto the beinging of the input array. * @return FfmpegProcess Returns the current object. * @throws \InvalidArgumentException If the input path does not exist. * @throws \InvalidArgumentException If the $index is not an integer. */ public function setInputPath($input, $index=null) { if(file_exists($input) === false || is_file($input) === false) { throw new \InvalidArgumentException('The input supplied `'.$input.'` is not a file or it does not exist.'); } if($index === null) { array_push($this->_input, $input); } else if(is_int($index) === false) { throw new \InvalidArgumentException('The input index must be an integer.'); } else if($index === -1) { array_unshift($this->_input, $input); } else { $this->_input[$index] = $input; } return $this; } /** * Gets the input path at the given index. * * @access public * @author Oliver Lillie * @param integer $index The index of which to return the input for. * @return string Returns the input from the requested index if exists. * @throws \InvalidArgumentException If the input index is not an integer. * @throws \LogicException If the input at the requested index does not exist. */ public function getInputPath($index=0) { if(is_int($index) === false) { throw new \InvalidArgumentException('The input index must be an integer.'); } if(isset($this->_input[$index]) === true) { return $this->_input[$index]; } throw new \LogicException('No input existed for given index `'.$index.'`'); } /** * Gets ALL the input given to the process. * * @access public * @author Oliver Lillie * @return array Returns an array of all the current input paths. */ public function getAllInput() { return $this->_input; } /** * Gets ALL the output given to the process. * * @access public * @author Oliver Lillie * @return array Returns an array of all the current output paths. */ public function getAllOutput() { return $this->_output; } /** * Sets the output at the current output index. * * @access public * @author Oliver Lillie * @param string $output The path to where the output media is to be saved to from ffmpeg. * @return FfmpegProcess Returns the current object. */ public function setOutputPath($output) { $this->_output[$this->_output_index] = $output; return $this; } /** * Returns the current number of output files that the FfmpegProcess object contains. * * @access public * @author: Oliver Lillie * @return integer */ public function getOutputCount() { return count($this->_output); } /** * Gets the output path at the index requested, or no index is request gets the output from the current index. * * @access public * @author: Oliver Lillie * @param integer $index The index of the output to return. If left null defaults to the currently incremented * index. * @return mixed If the index has not been set and the output does not exist then null is returned, if the output * does not exist but index has been set then a LogicException is thrown. If the output does exist then the string * path is returned. * @throws \InvalidArgumentException If the output index requested is not null and not an integer. * @throws \LogicException If the output does not exist and index has been set. */ public function getOutputPath($index=null) { $throw_exception_if_not_exist = false; if($index !== null && is_int($index) === false) { throw new \InvalidArgumentException('The output path index must be an integer.'); } else if($index === null) { $index = $this->_output_index; } else { $throw_exception_if_not_exist = true; } if($throw_exception_if_not_exist === true && isset($this->_output[$index]) === false) { throw new \LogicException('The requested output index has not been set.'); } return isset($this->_output[$index]) === true ? $this->_output[$index] : null; } /** * Adds a command to be bundled into command line call to be * added to the command line call before the input file is added. * * @access public * @author: Oliver Lillie * @param string $command The command to add. * @param mixed $argument Any optional arguments to add. If none, false should be given. * @param boolean $allow_command_repetition If this command can only be added once then set this to true to prevent * it from being added again. * @return FfmpegProcess Returns the current object. */ public function addPreInputCommand($command, $argument=false, $allow_command_repetition=false) { $this->_add($this->_pre_input_commands, $command, $argument, $allow_command_repetition); return $this; } /** * Adds a command to be bundled into command line call to be * added to the command line call after the input file is added. * * @access public * @author: Oliver Lillie * @param string $command The command to add. * @param mixed $argument Any optional arguments to add. If none, false should be given. * @param boolean $allow_command_repetition If this command can only be added once then set this to true to prevent * it from being added again. * @return FfmpegProcess Returns the current object. */ public function addCommand($command, $argument=false, $allow_command_repetition=false) { if(isset($this->_post_input_commands[$this->_output_index]) === false) { $this->_post_input_commands[$this->_output_index] = array(); } $this->_add($this->_post_input_commands[$this->_output_index], $command, $argument, $allow_command_repetition); return $this; } /** * Adds a command to be bundled into command line call to be * added to the command line call after the ouput file(s) is added. * * @access public * @author: Oliver Lillie * @param string $command The command to add. * @param mixed $argument Any optional arguments to add. If none, false should be given. * @param boolean $allow_command_repetition If this command can only be added once then set this to true to prevent * it from being added again. * @return FfmpegProcess Returns the current object. */ public function addPostOutputCommand($command, $argument=false, $allow_command_repetition=false) { $this->_add($this->_post_output_commands, $command, $argument, $allow_command_repetition); return $this; } /** * Removes a command from the pre input command list. * * @access public * @author: Oliver Lillie * @param string $command The command to add. * @param mixed $argument Any optional arguments to add. If none, false should be given. * @return FfmpegProcess Returns the current object. */ public function removePreInputCommand($command, $argument=false) { $this->_remove($this->_pre_input_commands, $command, $argument); return $this; } /** * Removes a command from the post input command list. * * @access public * @author: Oliver Lillie * @param string $command The command to add. * @param mixed $argument Any optional arguments to add. If none, false should be given. * @return FfmpegProcess Returns the current object. */ public function removeCommand($command, $argument=false) { $this->_remove($this->_post_input_commands, $command, $argument); return $this; } /** * Removes a command from the post output command list. * * @access public * @author: Oliver Lillie * @param string $command The command to add. * @param mixed $argument Any optional arguments to add. If none, false should be given. * @return FfmpegProcess Returns the current object. */ public function removePostOutputCommand($command, $argument=false) { $this->_remove($this->_post_output_commands, $command, $argument); return $this; } /** * Determines if the the command exits in the pre-input commands. * * @access public * @author: Oliver Lillie * @param string $command * @return mixed boolean If not found then false is returned. If found then the argument for that command (if any) is returned. Otherwise false is returned. */ public function hasPreInputCommand($command) { return isset($this->_pre_input_commands[$command]) === true ? ($this->_pre_input_commands[$command] === false ? true : $this->_pre_input_commands[$command]): false; } /** * Returns a pre input command. * * @access public * @author: Oliver Lillie * @param string $command * @return mixed If the command does not exist then false is returned, otherwise the command argument (if any is returned). */ public function getPreInputCommand($command) { if($this->hasPreInputCommand($command) === false) { return false; } return $this->_pre_input_commands[$command]; } /** * Determines if the the command exits. * * @access public * @author: Oliver Lillie * @param string $command * @param integer $index The index of the output to return. If left null defaults to the currently incremented * index. * @return mixed If the command does not exist then false is returned, otherwise the command argument (if any is returned). */ public function hasCommand($command, $index=null) { $index = $index === null ? $this->_output_index : $index; if(isset($this->_post_input_commands[$index]) === false) { return false; } return isset($this->_post_input_commands[$index][$command]) === true ? ($this->_post_input_commands[$index][$command] === false ? true : $this->_post_input_commands[$index][$command]): false; } /** * Returns an output command. * * @access public * @author: Oliver Lillie * @param string $command * @param integer $index The index of the output to return. If left null defaults to the currently incremented * index. * @return mixed If the command does not exist then false is returned, otherwise the command argument (if any is returned). */ public function getCommand($command, $index=null) { $index = $index === null ? $this->_output_index : $index; if($this->hasCommand($command, $index) === false) { return false; } return $this->_post_input_commands[$index][$command]; } /** * Determines if the post output command exits. * * @access public * @author: Oliver Lillie * @param string $command * @return mixed If the command does not exist then false is returned, otherwise the command argument (if any is returned). */ public function hasPostOutputCommand($command) { return isset($this->_post_output_commands[$command]) === true ? ($this->_post_output_commands[$command] === false ? true : $this->_post_output_commands[$command]): false; } /** * Returns a post output command. * * @access public * @author: Oliver Lillie * @param string $command * @return mixed If the command does not exist then false is returned, otherwise the command argument (if any is returned). */ public function getPostOutputCommand($command) { if($this->hasPostOutputCommand($command) === false) { return false; } return $this->_post_output_commands[$command]; } /** * Combines the commands stored into a string internaly. * * @access protected * @author: Oliver Lillie * @return void */ protected function _combineCommands() { if($this->_combined === true) { return; } $this->_combined = true; $args = $this->_arguments; $this->_arguments = array(); // add the pre input commands if(empty($this->_pre_input_commands) === false) { $this->addCommands($this->_pre_input_commands); } // add in the input if(empty($this->_input) === false) { foreach ($this->_input as $input) { $this->add('-i') ->add($input); } } // build the multiple post input and output path commands for($i=0; $i<=$this->_output_index; $i++) { // build the post input commands if(isset($this->_post_input_commands[$i]) === true && empty($this->_post_input_commands[$i]) === false) { $this->addCommands($this->_post_input_commands[$i]); } if(empty($args) === false) { $this->_arguments = array_merge($this->_arguments, $args); } // add in the output if(isset($this->_output[$i]) === true && empty($this->_output[$i]) === false) { $this->add($this->_output[$i]); } } // build the post output commands if(empty($this->_post_output_commands) === false) { $this->addCommands($this->_post_output_commands); } } /** * Returns the command string to be executed. * * @access public * @author Oliver Lillie * @return string */ public function getCommandString() { $this->_combineCommands(); return parent::getCommandString(); } /** * Get the ExecBuffer object by combining the commands the creating in the buffer. * * @access protected * @author Oliver Lillie * @return ExecBuffer */ protected function _getExecBuffer() { $this->_combineCommands(); return parent::getExecBuffer(); } /** * Get the initialised ExecBuffer object. * * @access public * @author Oliver Lillie * @return ExecBuffer */ public function &getExecBuffer() { if(empty($this->_exec) === true) { $this->_exec = $this->_getExecBuffer(); } return $this->_exec; } /** * Execute the buffer command. * * @access public * @author Oliver Lillie * @return self */ public function execute() { $this->getExecBuffer() ->setBlocking(true) ->execute(); return $this; } /** * Protected private function for calling functions from the ExecBuffer. * * @access protected * @author Oliver Lillie * @param string $function The name of the function to call on the ExecBuffer object. * @param array $arguments An array of arguments to supply to the called function. * @return mixed */ protected function _callExecBufferFunction($function, $arguments=array()) { // if no exec has been created then it has not completed. if(empty($this->_exec) === true) { return false; } if(is_callable(array($this->_exec, $function)) === false) { throw new FfmpegProcessException('This function is not callable within ExecBuffer.', $this->_exec, $this); } return call_user_func_array(array($this->_exec, $function), $arguments); } /** * Returns any "[xxx @ xxxxx] message" messages set in the buffer by FFmpeg. * * @access public * @author Oliver Lillie * @return array Returns an array of strings if any messages are present. */ public function getMessages() { $messages = array(); $buffer = $this->getBuffer(); if(empty($buffer) === false) { // 0x7f9db9065a00 if(preg_match_all('/\[([a-zA-Z0-9]+) @ (0x[a-z0-9]+)\] (.*)/', $buffer, $matches) > 0) { foreach ($matches[1] as $key=>$match) { if(isset($messages[$match]) === false) { $messages[$match] = array(); } if(isset($messages[$match][$matches[2][$key]]) === false) { $messages[$match][$matches[2][$key]] = array(); } array_push($messages[$match][$matches[2][$key]], $matches[3][$key]); } } } return $messages; } /** * Returns the current (or if called after isCompleted() returns true, the completed) * run time of the exec function. * * @access public * @author Oliver Lillie * @return mixed */ public function getRunTime() { return $this->_callExecBufferFunction('getRunTime'); } /** * Returns the buffers command or executed command. * * @access public * @author Oliver Lillie * @see ExecBuffer::getExecutedCommand * @param boolean $raw If true then the raw command is returned from the buffer, otherwise * the original command is returned. * @return mixed */ public function getExecutedCommand($raw=false) { return $this->_callExecBufferFunction($raw === false ? 'getCommand' : 'getExecutedCommand'); } /** * Returns the filtered buffer output of ExecBuffer. * * @access public * @author Oliver Lillie * @see ExecBuffer::getBuffer * @param boolean $raw If true then the raw command is returned from the buffer, otherwise * the original command is returned. * @return mixed */ public function getBuffer($raw=false) { return $this->_callExecBufferFunction($raw === false ? 'getBuffer' : 'getRawBuffer'); } /** * Returns the last line from the buffer output of ExecBuffer. * * @access public * @author Oliver Lillie * @see ExecBuffer::getLastLine * @return mixed */ public function getLastLine() { return $this->_callExecBufferFunction('getLastLine'); } /** * Returns the last split from the buffer output of ExecBuffer. * * @access public * @author Oliver Lillie * @see ExecBuffer::getLastSplit * @return mixed */ public function getLastSplit() { return $this->_callExecBufferFunction('getLastSplit'); } /** * Returns the error code encountered by the ExecBuffer. * * @access public * @author Oliver Lillie * @see ExecBuffer::getErrorCode * @return mixed */ public function getErrorCode() { return $this->_callExecBufferFunction('getErrorCode'); } /** * Returns a boolean value determining if the process has encountered an error. * Typically if this returns true, it also means the process has completed. * * @access public * @author Oliver Lillie * @see ExecBuffer::hasError * @param boolean $delete_output_on_error If true and an error has been encountered * and the output has been set and the output exists, then the output is deleted. * @return boolean */ public function hasError($delete_output_on_error=true) { $has_error = $this->_callExecBufferFunction('hasError'); // if we have an error and we want to delete any output on the error if($delete_output_on_error === true && $has_error === true) { foreach ($this->_output as $output) { if(empty($output) === false && is_file($output) === true) { @unlink($output); } } } return $has_error; } /** * Returns a boolean value determining if the process has completed. * * @access public * @author Oliver Lillie * @see ExecBuffer::isCompleted * @return boolean Returns true if the process is completed, otherwise false. */ public function isCompleted() { return $this->_callExecBufferFunction('isCompleted'); } /** * Returns the file name of the exec buffer output. * * @access public * @author Oliver Lillie * @see ExecBuffer::getBufferOutput * @return string */ public function getBufferOutput() { return $this->_callExecBufferFunction('getBufferOutput'); } /** * Returns a string value of a portable identifier used in conjunction with ProgressHandlerPortable. * WARNING. If this function is called it automatically disables the garbage collection of the ExceBuffer. * WARNING. This function should not be called directly. Please use Media::getPortableId instead. * * @access public * @author Oliver Lillie * @return string */ public function getPortableId() { if($this->_callExecBufferFunction('getBlocking') === true) { throw new \LogicException('It is not possible to get a portable id as the exec process has been made blocking. To get a portable id make the process unblocking or call getPortableId() before the save occurs.'); } $trace = debug_backtrace(); if(isset($trace[1]) === false || $trace[1]['function'] !== 'getPortableId' || $trace[1]['class'] !== 'PHPVideoToolkit\Media') { throw new \LogicException('Please call getPortableId from the media object rather than the process object, i.e. $video->getPortableId();'); } $this->_exec->setGarbageCollection(false); $output = $this->getBufferOutput(); return substr($output, strrpos($output, 'phpvideotoolkit_')+16).'.'.$this->_callExecBufferFunction('getBoundary').'.'.time(); } }