Ejemplo n.º 1
0
		/**
		 * 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
		 * @param $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.
		 *  }
		 */
		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 == PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE || $this->_overwrite_mode == PHPVIDEOTOOLKIT_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 PHPVIDEOTOOLKIT_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 PHPVIDEOTOOLKIT_OVERWRITE_EXISTING :
// 					add an overwrite command to ffmpeg execution call
					$overwrite = '-y ';
					break;
					
				case PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE :
// 					do nothing as the preservation comes later
					break;
					
				case PHPVIDEOTOOLKIT_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 = PHPVideoToolkit::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);
			}
			
//			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);
			}
			
// 			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 === PHPVIDEOTOOLKIT_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']) && isset($info['video']['dimensions']))
					{
						$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]);
						}
					}
				}
			}

//			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(PHPVIDEOTOOLKIT_FFMPEG_BINARY, $command_string, $overwrite.escapeshellcmd($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);
				$exec_string = $exec_string.' &> '.$this->_log_file;
			}
			
//			execute the command
			exec($exec_string);
			
//			track the processed command by adding it to the class
			array_unshift($this->_processed, $exec_string);
			
// 			create the multiple pass encode
			if($multi_pass_encode)
			{
				$pass2_exc_string = str_replace('-pass '.escapeshellarg(1), '-pass '.escapeshellarg(2), $exec_string);
				exec($pass2_exc_string);
				$this->_processed[0] = array($this->_processed[0], $pass2_exc_string);
// 				remove the multipass log file
				unlink($multi_pass_file.'-0.log');
			}
// 			keep track of the time taken
			$execution_time = PHPVideoToolkit::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, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_command'), $this->_getMessage('ffmpeg_log_separator'));
				array_unshift($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_gunk'), $this->_getMessage('ffmpeg_log_separator'));
				$this->_addToLog($lines, 'r+');
			}
// 			exit
//			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]) ? 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;
				
// 				if(!$use_timecode && $use_index)
// 				{
// 					if($log)
// 					{
// 						$this->_logResult('execute_overwrite_fail');
// 					}
// 					return $this->_raiseError('execute_overwrite_fail');
// 				}
				
// 				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]) ? $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 == PHPVIDEOTOOLKIT_OVERWRITE_EXISTING)
					{
						rename($filename, $new_filename);
						$filename = $new_filename;
					}
// 					the file exists and is not allowed to be overriden so just rename in the temp directory using the timecode
					else if($this->_overwrite_mode == PHPVIDEOTOOLKIT_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;
					}
// 					the file exists so the process must fail
					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;
// 				if the result is false then no post process has taken place

				$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, 'r+');
				
				return $file_exists ? PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE : PHPVIDEOTOOLKIT_RESULT_OK;
			}
//			must validate one file
			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 == PHPVIDEOTOOLKIT_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

// 					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, 'r+');
						
// 						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 PHPVIDEOTOOLKIT_RESULT_OK;
					}
// 					renaming failed so return ok but erro
					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, 'r+');
						return PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE;
					}
				}
// 				if it is not we signal that it has been created but has not been moved.
				else if($this->_overwrite_mode == PHPVIDEOTOOLKIT_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 PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE;
				}
// 				the file exists so the process must fail
				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;

		}