public function PHPVideoToolkit::execute in Video 7
Same name and namespace in other branches
- 7.2 libraries/phpvideotoolkit/phpvideotoolkit.php5.php \PHPVideoToolkit::execute()
* Commits all the commands and executes the ffmpeg procedure. This will also attempt to validate any outputted files in order to provide * some level of stop and check system. * * @access public *
Parameters
$multi_pass_encode boolean Determines if multi (2) pass encoding should be used.: * @param $log boolean Determines if a log file of the results should be generated. * @return mixed * - false On error encountered. * - PHPVideoToolkit::RESULT_OK (bool true) If the file has successfully been processed and moved ok to the output address * - PHPVideoToolkit::RESULT_OK_BUT_UNWRITABLE (int -1) If the file has successfully been processed but was not able to be moved correctly to the output address * If this is the case you will manually need to move the processed file from the temp directory. You can * get around this by settings the third argument from PHPVideoToolkit::setOutput(), $overwrite to true. * - n (int) A positive integer is only returned when outputting a series of frame grabs from a movie. It dictates * the total number of frames grabbed from the input video. You should also not however, that if a conflict exists * with one of the filenames then this return value will not be returned, but PHPVideoToolkit::RESULT_OK_BUT_UNWRITABLE * will be returned instead. * Because of the mixed return value you should always go a strict evaluation of the returned value. ie * * $result = $toolkit->excecute(); * if($result === false) * { * // error * } * else if($result === PHPVideoToolkit::RESULT_OK_BUT_UNWRITABLE) * { * // ok but a manual move is required. The file to move can be it can be retrieved by $toolkit->getLastOutput(); * } * else if($result === PHPVideoToolkit::RESULT_OK) * { * // everything is ok. * }
File
- libraries/
phpvideotoolkit/ phpvideotoolkit.php5.php, line 3018
Class
Code
public function execute($multi_pass_encode = false, $log = false) {
// check for inut and output params
$has_placeholder = preg_match('/\\%([0-9]+)index/', $this->_process_address) || strpos($this->_process_address, '%index') === false && strpos($this->_process_address, '%timecode') === false;
if ($this->_input_file === null && !$has_placeholder) {
return $this
->_raiseError('execute_input_404');
//<- exits
}
// check to see if the output address has been set
if ($this->_process_address === null) {
return $this
->_raiseError('execute_output_not_set');
//<- exits
}
// check if temp dir is required and is writable
if (($multi_pass_encode || $log) && !is_writable($this->_tmp_directory)) {
return $this
->_raiseError('execute_temp_unwritable');
//<- exits
}
if (($this->_overwrite_mode == self::OVERWRITE_PRESERVE || $this->_overwrite_mode == self::OVERWRITE_FAIL) && is_file($this->_process_address)) {
return $this
->_raiseError('execute_overwrite_process');
//<- exits
}
// carry out some overwrite checks if required
$overwrite = '';
switch ($this->_overwrite_mode) {
case self::OVERWRITE_UNIQUE:
// insert a unique id into the output address (the process address already has one)
$unique = $this
->unique();
$last_index = strrpos($this->_output_address, DS);
$this->_output_address = substr($this->_output_address, 0, $last_index + 1) . $unique . '-' . substr($this->_output_address, $last_index + 1);
break;
case self::OVERWRITE_EXISTING:
// add an overwrite command to ffmpeg execution call
$overwrite = '-y ';
break;
case self::OVERWRITE_PRESERVE:
// do nothing as the preservation comes later
break;
case self::OVERWRITE_FAIL:
default:
// if the file should fail
if (!$has_placeholder && is_file($this->_output_address)) {
return $this
->_raiseError('execute_overwrite_fail');
//<- exits
}
break;
}
$this->_timer_start = self::microtimeFloat();
// we have multiple inputs that require joining so convert them to a joinable format and join
if (is_array($this->_input_file)) {
$this
->_joinInput($log);
}
// check to see if the format has been set and if it hasn't been set and the extension is a gif
// we need to add an extra argument to set the pix format.
$format = $this
->hasCommand('-f');
if ($format === false) {
$extension = strtolower(array_pop(explode('.', $this->_input_file)));
if ($extension === 'gif') {
$this
->addCommand('-pix_fmt', 'rgb24');
}
}
else {
if ($format === self::FORMAT_GIF) {
$this
->addCommand('-pix_fmt', 'rgb24');
}
}
// check to see if an aspect ratio is set, if it is correct the width and heights to reflect that aspect ratio.
// This isn't strictly needed it is purely for informational purposes that this is done, because if the width is not
// inline with what is should be according to the aspect ratio ffmpeg will report the wrong final width and height
// when using it to lookup information about the file.
$ratio = $this
->hasCommand('-aspect');
if ($ratio !== false) {
$size = $this
->hasCommand('-s');
if ($size === false) {
$info = $this
->getFileInfo();
if (isset($info['video']) === true && isset($info['video']['dimensions']) === true) {
$size = $info['video']['dimensions']['width'] . 'x' . $info['video']['dimensions']['height'];
}
}
if ($size !== false) {
$dim = explode('x', substr($size, 1, -1));
if (($boundry = strpos($ratio, ':')) !== false) {
$ratio = substr($ratio, 1, $boundry - 1) / substr($ratio, $boundry + 1, -1);
$new_width = round($dim[1] * $ratio);
// make sure new width is an even number
$ceiled = ceil($new_width);
$new_width = $ceiled % 2 !== 0 ? floor($new_width) : $ceiled;
if ($new_width != $dim[0]) {
$this
->setVideoDimensions($new_width, $dim[1]);
}
}
else {
if (strpos($ratio, '.') !== false) {
$ratio = floatval($ratio);
$new_width = $dim[1] * $ratio;
// make sure new width is an even number
$ceiled = ceil($new_width);
$new_width = $ceiled % 2 !== 0 ? floor($new_width) : $ceiled;
if ($new_width != $dim[0]) {
$this
->setVideoDimensions($new_width, $dim[1]);
}
}
}
}
}
// add the input file command to the mix
$this
->addCommand('-i', $this->_input_file);
// if multi pass encoding is enabled add the commands and logfile
if ($multi_pass_encode) {
$multi_pass_file = $this->_tmp_directory . $this
->unique() . '-multipass';
$this
->addCommand('-pass', 1);
$this
->addCommand('-passlogfile', $multi_pass_file);
}
// combine all the output commands
$command_string = $this
->_combineCommands();
// prepare the command suitable for exec
// the input and overwrite commands have specific places to be set so they have to be added outside of the combineCommands function
$exec_string = $this
->_prepareCommand($this->_ffmpeg_binary, $command_string, $overwrite . $this->_process_address);
// $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FFMPEG_BINARY, '-i '.$this->_commands['-i'].' '.$command_string, $overwrite.escapeshellcmd($this->_process_address));
if ($log) {
$this->_log_file = $this->_tmp_directory . $this
->unique() . '.info';
array_push($this->_unlink_files, $this->_log_file);
}
// execute the command
// $exec_string = $exec_string.' 2>&1';// &> '.$this->_log_file;
$buffer = self::_captureExecBuffer($exec_string, $this->_tmp_directory);
// exec($exec_string, $buffer);
if ($log) {
$this
->_addToLog($buffer, 'a+');
}
// track the processed command by adding it to the class
array_unshift($this->_processed, $exec_string);
// scan buffer for any errors
$last_line = $buffer[count($buffer) - 1];
if (preg_match('/(.*)(Unsupported codec|Error while opening)(.*)/s', $last_line, $error_matches) > 0) {
$type = $error_matches[2];
switch ($error_matches[2]) {
case 'Unsupported codec':
break;
case 'Error while opening':
break;
}
$stream = 'could be with either the audio or video codec';
if (preg_match('/#0.(0|1)/', $last_line, $stream_matches) > 0) {
$stream = $stream_matches[1] === '0' ? 'is with the video codec' : 'is with the audio codec';
}
// add the error to the log file
if ($log) {
$this
->_logResult('execute_ffmpeg_return_error', array(
'input' => $this->_input_file,
'type' => $type,
'message' => $error_matches[0],
'stream' => $stream,
));
}
return $this
->_raiseError('execute_ffmpeg_return_error', array(
'input' => $this->_input_file,
'type' => $type,
'message' => $error_matches[0],
'stream' => $stream,
));
}
// create the multiple pass encode
if ($multi_pass_encode) {
$pass2_exc_string = str_replace('-pass ' . escapeshellarg(1), '-pass ' . escapeshellarg(2), $exec_string);
$buffer = self::_captureExecBuffer($pass2_exc_string, $this->_tmp_directory);
// exec($pass2_exc_string, $buffer);
if ($log) {
$this
->_addToLog($buffer, 'a+');
}
$this->_processed[0] = array(
$this->_processed[0],
$pass2_exc_string,
);
// tidy up the multipass log file
array_push($this->_unlink_files, $multi_pass_file . '-0.log');
// scan buffer for any errors
$last_line = $buffer[count($buffer) - 1];
if (preg_match('/(.*)(Unsupported codec|Error while opening)(.*)/s', $last_line, $error_matches) > 0) {
$type = $error_matches[2];
switch ($error_matches[2]) {
case 'Unsupported codec':
break;
case 'Error while opening':
break;
}
$stream = 'could be with either the audio or video codec';
if (preg_match('/#0.(0|1)/', $last_line, $stream_matches) > 0) {
$stream = $stream_matches[1] === '0' ? 'is with the video codec' : 'is with the audio codec';
}
// add the error to the log file
if ($log) {
$this
->_logResult('execute_ffmpeg_return_error_multipass', array(
'input' => $this->_input_file,
'type' => $type,
'message' => $error_matches[0],
'stream' => $stream,
));
}
return $this
->_raiseError('execute_ffmpeg_return_error_multipass', array(
'input' => $this->_input_file,
'type' => $type,
'message' => $error_matches[0],
'stream' => $stream,
));
}
}
// keep track of the time taken
$execution_time = self::microtimeFloat() - $this->_timer_start;
array_unshift($this->_timers, $execution_time);
// add the exec string to the log file
if ($log) {
$lines = $this->_processed[0];
if (!is_array($lines)) {
$lines = array(
$lines,
);
}
// array_unshift($lines, $exec_string);
array_unshift($lines, $this
->_getMessage('ffmpeg_log_separator'), $this
->_getMessage('ffmpeg_log_ffmpeg_command'), $this
->_getMessage('ffmpeg_log_separator'));
// if($multi_pass_encode)
// {
// array_unshift($lines, $pass2_exc_string);
// }
array_unshift($lines, $this
->_getMessage('ffmpeg_log_separator'), $this
->_getMessage('ffmpeg_log_ffmpeg_gunk'), $this
->_getMessage('ffmpeg_log_separator'));
$this
->_addToLog($lines, 'a+');
}
// must validate a series of outputed items
// detect if the output address is a sequence output
if (preg_match('/\\%([0-9]+)d/', $this->_process_address, $d_matches) || strpos($this->_process_address, '%d') !== false) {
// get the path details
$process_info = pathinfo($this->_process_address);
$output_info = pathinfo($this->_output_address);
$pad_amount = intval($d_matches[1]);
// print_r(array($process_info, $output_info));
// get the %index padd amounts
$has_preg_index = preg_match('/\\%([0-9]+)index/', $output_info['basename'], $index_matches);
$output_index_pad_amount = isset($index_matches[1]) === true ? intval($index_matches[1], 1) : 0;
// var_dump($index_matches);
// init the iteration values
$num = 1;
$files = array();
$produced = array();
$error = false;
$name_conflict = false;
$file_exists = false;
// get the first files name
$filename = $process_info['dirname'] . DS . str_replace($d_matches[0], str_pad($num, $pad_amount, '0', STR_PAD_LEFT), $process_info['basename']);
$use_timecode = strpos($output_info['basename'], '%timecode') !== false;
$use_index = $has_preg_index || strpos($output_info['basename'], '%index') !== false;
// start the timecode pattern replacement values
if ($use_timecode) {
$secs_start = $this
->formatTimecode($this->_image_output_timecode_start, '%hh:%mm:%ss.%ms', '%mt', $this->_image_output_timecode_fps);
$fps_inc = 1 / $this->_image_output_timecode_fps;
$fps_current_sec = 0;
$fps_current_frame = 0;
}
// loop checking for file existence
while (@is_file($filename)) {
// check for empty file
$size = filesize($filename);
if ($size == 0) {
$error = true;
}
array_push($produced, $filename);
// create the substitution arrays
$searches = array();
$replacements = array();
if ($use_index) {
array_push($searches, isset($index_matches[0]) === true ? $index_matches[0] : '%index');
array_push($replacements, str_pad($num, $output_index_pad_amount, '0', STR_PAD_LEFT));
}
// check if timecode is in the output name, no need to use it if not
if ($use_timecode) {
$fps_current_sec += $fps_inc;
$fps_current_frame += 1;
if ($fps_current_sec >= 1) {
$fps_current_sec = $fps_inc;
$secs_start += 1;
$fps_current_frame = 1;
}
$timecode = $this
->formatSeconds($secs_start, $this->image_output_timecode_format, $this->_image_output_timecode_fps);
$timecode = str_replace(array(
':',
'.',
), $this->timecode_seperator_output, $timecode);
// add to the substitution array
array_push($searches, '%timecode');
array_push($replacements, $timecode);
}
// check if the file exists already and if it does check that it can be overriden
$old_filename = $filename;
// print_r(array($searches, $replacements, $output_info['basename']));
$new_file = str_replace($searches, $replacements, $output_info['basename']);
$new_filename = $output_info['dirname'] . DS . $new_file;
// var_dump($filename, $new_filename);
if (!is_file($new_filename) || $this->_overwrite_mode == self::OVERWRITE_EXISTING) {
if (is_file($new_filename)) {
unlink($new_filename);
}
rename($filename, $new_filename);
$filename = $new_filename;
}
else {
if ($this->_overwrite_mode == self::OVERWRITE_PRESERVE) {
$new_filename = $process_info['dirname'] . DS . 'tbm-' . $this
->unique() . '-' . $new_file;
rename($filename, $new_filename);
$filename = $new_filename;
// add the error to the log file
if ($log) {
$this
->_logResult('execute_image_file_exists', array(
'file' => $new_filename,
));
}
// flag the conflict
$file_exists = true;
}
else {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_overwrite_fail');
}
// tidy up the produced files
array_merge($this->_unlink_files, $produced);
return $this
->_raiseError('execute_overwrite_fail');
}
}
// process the name change if the %d is to be replaced with the timecode
$num += 1;
$files[$filename] = $size > 0 ? basename($filename) : false;
// print_r("\r\n\r\n".is_file($old_filename)." - ".$old_filename.' => '.$new_filename);
// print_r($files);
// get the next incremented filename to check for existance
$filename = $process_info['dirname'] . DS . str_replace($d_matches[0], str_pad($num, $pad_amount, '0', STR_PAD_LEFT), $process_info['basename']);
}
// de-increment the last num as it wasn't found
$num -= 1;
// if the file was detected but were empty then display a different error
if ($error === true) {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_partial_error', array(
'input' => $this->_input_file,
));
}
return $this
->_raiseError('execute_partial_error', array(
'input' => $this->_input_file,
));
//<- exits
}
// post process any files
// print_r($files);
$post_process_result = $this
->_postProcess($log, $files);
// print_r($files);
if (is_array($post_process_result)) {
// post process has occurred and everything is fine
$num = count($files);
}
else {
if ($post_process_result !== false) {
// the file has encountered an error in the post processing of the files
return $post_process_result;
}
}
// var_dump("\r\n\r\n", $files, __LINE__, __FILE__, "\r\n\r\n"); exit;
$this->_process_file_count = $num;
// no files were generated in this sequence
if ($num == 0) {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_image_error', array(
'input' => $this->_input_file,
));
}
return $this
->_raiseError('execute_image_error', array(
'input' => $this->_input_file,
));
//<- exits
}
// add the files the the class a record of what has been generated
array_unshift($this->_files, $files);
array_push($lines, $this
->_getMessage('ffmpeg_log_separator'), $this
->_getMessage('ffmpeg_log_ffmpeg_output'), $this
->_getMessage('ffmpeg_log_separator'), implode("\n", $files));
$this
->_addToLog($lines, 'a+');
return $file_exists ? self::RESULT_OK_BUT_UNWRITABLE : self::RESULT_OK;
}
else {
// check that it is a file
if (!is_file($this->_process_address)) {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_output_404', array(
'input' => $this->_input_file,
));
}
return $this
->_raiseError('execute_output_404', array(
'input' => $this->_input_file,
));
//<- exits
}
// the file does exist but is it empty?
if (filesize($this->_process_address) == 0) {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_output_empty', array(
'input' => $this->_input_file,
));
}
return $this
->_raiseError('execute_output_empty', array(
'input' => $this->_input_file,
));
//<- exits
}
// the file is ok so move to output address
if (!is_file($this->_output_address) || $this->_overwrite_mode == self::OVERWRITE_EXISTING) {
// post process any files
$post_process_result = $this
->_postProcess($log, array(
$this->_process_address,
));
if (is_array($post_process_result) || $post_process_result === true) {
// post process has occurred and everything is fine
}
else {
if ($post_process_result !== false) {
return $post_process_result;
}
}
// if the result is false then no post process has taken place
if (is_file($this->_output_address)) {
unlink($this->_output_address);
}
// rename the file to the final destination and check it went ok
if (rename($this->_process_address, $this->_output_address)) {
array_push($lines, $this
->_getMessage('ffmpeg_log_separator'), $this
->_getMessage('ffmpeg_log_ffmpeg_output'), $this
->_getMessage('ffmpeg_log_separator'), $this->_output_address);
$this
->_addToLog($lines, 'a+');
// the file has been renamed ok
// add the error to the log file
if ($log) {
$this
->_logResult('execute_result_ok', array(
'output' => $this->_output_address,
));
}
$this->_process_file_count = 1;
// add the file the the class a record of what has been generated
array_unshift($this->_files, array(
$this->_output_address,
));
return self::RESULT_OK;
}
else {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_result_ok_but_unwritable', array(
'process' => $this->_process_address,
'output' => $this->_output_address,
));
}
// add the file the the class a record of what has been generated
array_unshift($this->_files, array(
$this->_process_address,
));
array_push($lines, $this
->_getMessage('ffmpeg_log_separator'), $this
->_getMessage('ffmpeg_log_ffmpeg_output'), $this
->_getMessage('ffmpeg_log_separator'), $this->_process_address);
$this
->_addToLog($lines, 'a+');
return self::RESULT_OK_BUT_UNWRITABLE;
}
}
else {
if ($this->_overwrite_mode == self::OVERWRITE_PRESERVE) {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_result_ok_but_unwritable', array(
'process' => $this->_process_address,
'output' => $this->_output_address,
));
}
// add the file the the class a record of what has been generated
array_unshift($this->_files, array(
$this->_process_address,
));
return self::RESULT_OK_BUT_UNWRITABLE;
}
else {
// add the error to the log file
if ($log) {
$this
->_logResult('execute_overwrite_fail');
}
// tidy up the produced files
array_push($this->_unlink_files, $this->_process_address);
return $this
->_raiseError('execute_overwrite_fail');
}
}
}
return null;
}