PageRenderTime 55ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/modules/video/libraries/phpvideotoolkit/phpvideotoolkit.php4.php

https://bitbucket.org/becomplete/enrollment123
PHP | 3599 lines | 1939 code | 185 blank | 1475 comment | 308 complexity | 4c6e845bbcc0b7211b8420958005a721 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * @author Oliver Lillie (aka buggedcom) <publicmail@buggedcom.co.uk>
  5. *
  6. * @license BSD
  7. * @copyright Copyright (c) 2008 Oliver Lillie <http://www.buggedcom.co.uk>
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
  11. * is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be
  12. * included in all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  15. * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  16. * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  17. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. *
  19. * @package PHPVideoToolkit (was called ffmpeg)
  20. * @version 0.1.8
  21. * @changelog SEE CHANGELOG
  22. * @abstract This class can be used in conjunction with several server binary libraries to manipulate video and audio
  23. * through PHP. It is not intended to solve any particular problems, however you may find it useful. This php class
  24. * is in no way associated with the actual FFmpeg releases. Any mistakes contained in this php class are mine and mine
  25. * alone.
  26. *
  27. * Please Note: There are several prerequisites that are required before this class can be used as an aid to manipulate
  28. * video and audio. You must at the very least have FFMPEG compiled on your server. If you wish to use this class for FLV
  29. * manipulation you must compile FFMPEG with LAME and Ruby's FLVTOOL2. I cannot answer questions regarding the install of
  30. * the server binaries needed by this class. I had too learn the hard way and it isn't easy, however it is a good learning
  31. * experience. For those of you who do need help read the install.txt file supplied along side this class. It wasn't written
  32. * by me however I found it useful when installing ffmpeg for the first time. The original source for the install.txt file
  33. * is located http://www.luar.com.hk/blog/?p=669 and the author is Lunar.
  34. *
  35. * @see install.txt
  36. *
  37. * @uses ffmpeg http://ffmpeg.sourceforge.net/
  38. * @uses lame http://lame.sourceforge.net/
  39. * @uses flvtool2 http://www.inlet-media.de/flvtool2 (and ruby http://www.ruby-lang.org/en/)
  40. *
  41. * @config examples/example-config.php Please edit this files in order for the examples to work.
  42. * @example examples/example01.php Converts video to Flash Video (ie FLV).
  43. * @example examples/example02.php Screen grabs video frames.
  44. * @example examples/example03.php Compile a movie from multiple jpegs
  45. * @example examples/example04.php Watermark a video.
  46. * @example examples/example05.php Access media metadata without using the ffmpeg-php library.
  47. * @example examples/example06.php Extract audio from video.
  48. * @example examples/example07.php Join multiple videos together.
  49. * @example examples/example08.php Easy video conversion to common formats using the adapters.
  50. * @example examples/example09.php Shows you how to access the information about your ffmpeg installation.
  51. * @example examples/example10.php Shows you how to extract a specific frame from a movie.
  52. * @example examples/example11.php Shows you how to use the ffmpeg-php adapters to provide a pure php emulation of ffmpeg-php.
  53. * @example examples/example12.php Shows you how to manipulate/format timecode strings.
  54. * @example examples/example13.php This demonstrates how to simply create a FLV stream script.
  55. */
  56. if(!defined('DS'))
  57. {
  58. define('DS', DIRECTORY_SEPARATOR);
  59. }
  60. /**
  61. * Set the ffmpeg binary path
  62. */
  63. if(!defined('PHPVIDEOTOOLKIT_FFMPEG_BINARY'))
  64. {
  65. define('PHPVIDEOTOOLKIT_FFMPEG_BINARY', '/usr/local/bin/ffmpeg');
  66. }
  67. /**
  68. * Set the flvtool2 binary path
  69. */
  70. if(!defined('PHPVIDEOTOOLKIT_FLVTOOLS_BINARY'))
  71. {
  72. define('PHPVIDEOTOOLKIT_FLVTOOLS_BINARY', '/usr/bin/flvtool2');
  73. }
  74. /**
  75. * Set the watermark vhook path
  76. */
  77. if(!defined('PHPVIDEOTOOLKIT_FFMPEG_WATERMARK_VHOOK'))
  78. {
  79. define('PHPVIDEOTOOLKIT_FFMPEG_WATERMARK_VHOOK', '/usr/local/lib/vhook/watermark.so');
  80. }
  81. /**
  82. * Set the memcoder path
  83. */
  84. if(!defined('PHPVIDEOTOOLKIT_MENCODER_BINARY'))
  85. {
  86. define('PHPVIDEOTOOLKIT_MENCODER_BINARY', '/usr/local/bin/mencoder');
  87. }
  88. /**
  89. * Codec support constants
  90. */
  91. define('PHPVIDEOTOOLKIT_ENCODE', 'encode');
  92. define('PHPVIDEOTOOLKIT_DECODE', 'decode');
  93. /**
  94. * Process Results from PHPVideoToolkit::execute
  95. */
  96. // any return value with this means everything is ok
  97. define('PHPVIDEOTOOLKIT_RESULT_OK', true);
  98. // any return value with this means the file has been processed/converted ok however it was
  99. // not able to be written to the output address. If this occurs you will need to move the
  100. // processed file manually from the temp location
  101. define('PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE', -1);
  102. /**
  103. * Overwrite constants used in setOutput
  104. */
  105. define('PHPVIDEOTOOLKIT_OVERWRITE_FAIL', 'fail');
  106. define('PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE', 'preserve');
  107. define('PHPVIDEOTOOLKIT_OVERWRITE_EXISTING', 'existing');
  108. define('PHPVIDEOTOOLKIT_OVERWRITE_UNIQUE', 'unique');
  109. /**
  110. * Formats supported
  111. * 3g2 3gp2 format
  112. * 3gp 3gp format
  113. * aac ADTS AAC
  114. * aiff Audio IFF
  115. * amr 3gpp amr file format
  116. * asf asf format
  117. * avi avi format
  118. * flv flv format
  119. * gif GIF Animation
  120. * mov mov format
  121. * mov,mp4,m4a,3gp,3g2,mj2 QuickTime/MPEG4/Motion JPEG 2000 format
  122. * mp2 MPEG audio layer 2
  123. * mp3 MPEG audio layer 3
  124. * mp4 mp4 format
  125. * mpeg MPEG1 System format
  126. * mpeg1video MPEG video
  127. * mpeg2video MPEG2 video
  128. * mpegvideo MPEG video
  129. * psp psp mp4 format
  130. * rm rm format
  131. * swf Flash format
  132. * vob MPEG2 PS format (VOB)
  133. * wav wav format
  134. * jpeg mjpeg format
  135. * yuv4mpegpipe yuv4mpegpipe format
  136. */
  137. define('PHPVIDEOTOOLKIT_FORMAT_3GP2', '3g2');
  138. define('PHPVIDEOTOOLKIT_FORMAT_3GP', '3gp');
  139. define('PHPVIDEOTOOLKIT_FORMAT_AAC', 'aac');
  140. define('PHPVIDEOTOOLKIT_FORMAT_AIFF', 'aiff');
  141. define('PHPVIDEOTOOLKIT_FORMAT_AMR', 'amr');
  142. define('PHPVIDEOTOOLKIT_FORMAT_ASF', 'asf');
  143. define('PHPVIDEOTOOLKIT_FORMAT_AVI', 'avi');
  144. define('PHPVIDEOTOOLKIT_FORMAT_FLV', 'flv');
  145. define('PHPVIDEOTOOLKIT_FORMAT_GIF', 'gif');
  146. define('PHPVIDEOTOOLKIT_FORMAT_MJ2', 'mj2');
  147. define('PHPVIDEOTOOLKIT_FORMAT_MP2', 'mp2');
  148. define('PHPVIDEOTOOLKIT_FORMAT_MP3', 'mp3');
  149. define('PHPVIDEOTOOLKIT_FORMAT_MP4', 'mp4');
  150. define('PHPVIDEOTOOLKIT_FORMAT_MPEG4', 'mpeg4');
  151. define('PHPVIDEOTOOLKIT_FORMAT_M4A', 'm4a');
  152. define('PHPVIDEOTOOLKIT_FORMAT_MPEG', 'mpeg');
  153. define('PHPVIDEOTOOLKIT_FORMAT_MPEG1', 'mpeg1video');
  154. define('PHPVIDEOTOOLKIT_FORMAT_MPEG2', 'mpeg2video');
  155. define('PHPVIDEOTOOLKIT_FORMAT_MPEGVIDEO', 'mpegvideo');
  156. define('PHPVIDEOTOOLKIT_FORMAT_PSP', 'psp');
  157. define('PHPVIDEOTOOLKIT_FORMAT_RM', 'rm');
  158. define('PHPVIDEOTOOLKIT_FORMAT_SWF', 'swf');
  159. define('PHPVIDEOTOOLKIT_FORMAT_VOB', 'vob');
  160. define('PHPVIDEOTOOLKIT_FORMAT_WAV', 'wav');
  161. define('PHPVIDEOTOOLKIT_FORMAT_JPG', 'mjpeg');
  162. define('PHPVIDEOTOOLKIT_FORMAT_Y4MP', 'yuv4mpegpipe');
  163. /**
  164. * Size Presets
  165. */
  166. define('PHPVIDEOTOOLKIT_SIZE_SAS', 'SameAsSource');
  167. define('PHPVIDEOTOOLKIT_SIZE_SQCIF', '128x96');
  168. define('PHPVIDEOTOOLKIT_SIZE_QCIF', '176x144');
  169. define('PHPVIDEOTOOLKIT_SIZE_CIF', '352x288');
  170. define('PHPVIDEOTOOLKIT_SIZE_4CIF', '704x576');
  171. define('PHPVIDEOTOOLKIT_SIZE_QQVGA', '160x120');
  172. define('PHPVIDEOTOOLKIT_SIZE_QVGA', '320x240');
  173. define('PHPVIDEOTOOLKIT_SIZE_VGA', '640x480');
  174. define('PHPVIDEOTOOLKIT_SIZE_SVGA', '800x600');
  175. define('PHPVIDEOTOOLKIT_SIZE_XGA', '1024x768');
  176. define('PHPVIDEOTOOLKIT_SIZE_UXGA', '1600x1200');
  177. define('PHPVIDEOTOOLKIT_SIZE_QXGA', '2048x1536');
  178. define('PHPVIDEOTOOLKIT_SIZE_SXGA', '1280x1024');
  179. define('PHPVIDEOTOOLKIT_SIZE_QSXGA', '2560x2048');
  180. define('PHPVIDEOTOOLKIT_SIZE_HSXGA', '5120x4096');
  181. define('PHPVIDEOTOOLKIT_SIZE_WVGA', '852x480');
  182. define('PHPVIDEOTOOLKIT_SIZE_WXGA', '1366x768');
  183. define('PHPVIDEOTOOLKIT_SIZE_WSXGA', '1600x1024');
  184. define('PHPVIDEOTOOLKIT_SIZE_WUXGA', '1920x1200');
  185. define('PHPVIDEOTOOLKIT_SIZE_WOXGA', '2560x1600');
  186. define('PHPVIDEOTOOLKIT_SIZE_WQSXGA', '3200x2048');
  187. define('PHPVIDEOTOOLKIT_SIZE_WQUXGA', '3840x2400');
  188. define('PHPVIDEOTOOLKIT_SIZE_WHSXGA', '6400x4096');
  189. define('PHPVIDEOTOOLKIT_SIZE_WHUXGA', '7680x4800');
  190. define('PHPVIDEOTOOLKIT_SIZE_CGA', '320x200');
  191. define('PHPVIDEOTOOLKIT_SIZE_EGA', '640x350');
  192. define('PHPVIDEOTOOLKIT_SIZE_HD480', '852x480');
  193. define('PHPVIDEOTOOLKIT_SIZE_HD720', '1280x720');
  194. define('PHPVIDEOTOOLKIT_SIZE_HD1080', '1920x1080');
  195. /**
  196. * Ratio Presets
  197. */
  198. define('PHPVIDEOTOOLKIT_RATIO_STANDARD', '4:3');
  199. define('PHPVIDEOTOOLKIT_RATIO_WIDE', '16:9');
  200. define('PHPVIDEOTOOLKIT_RATIO_CINEMATIC', '1.85');
  201. /**
  202. * Audio Channel Presets
  203. */
  204. define('PHPVIDEOTOOLKIT_AUDIO_STEREO', 2);
  205. define('PHPVIDEOTOOLKIT_AUDIO_MONO', 1);
  206. /**
  207. * @author Oliver Lillie (aka buggedcom) <publicmail@buggedcom.co.uk>
  208. * @license BSD
  209. * @package PHPVideoToolkit (was called ffmpeg)
  210. */
  211. class PHPVideoToolkit
  212. {
  213. var $version = '0.1.8';
  214. /**
  215. * Error strings
  216. */
  217. var $_messages = array(
  218. 'generic_temp_404' => 'The temporary directory does not exist.',
  219. 'generic_temp_writable' => 'The temporary directory is not write-able by the web server.',
  220. 'getFileInfo_no_input' => 'Input file does not exist so no information can be retrieved.',
  221. 'inputFileHasVideo_no_input' => 'Input file does not exist so no information can be retrieved.',
  222. 'inputFileHasAudio_no_input' => 'Input file does not exist so no information can be retrieved.',
  223. 'setInputFile_file_existence' => 'Input file "#file" does not exist',
  224. 'extractAudio_valid_format' => 'Value "#format" set from $toolkit->extractAudio, is not a valid audio format. Valid values ffmpeg PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG4, PHPVIDEOTOOLKIT_FORMAT_M4A or PHPVIDEOTOOLKIT_FORMAT_WAV. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-f" and $argument to your required value.',
  225. 'extractFrame_video_frame_rate_404' => 'You have attempted to extract a thumbnail from a video while automagically guessing the framerate of the video, but the framerate could not be accessed. You can remove this error by manually setting the frame rate of the video.',
  226. 'extractFrame_video_info_404' => 'You have attempted to extract a thumbnail from a video and check to see if the thumbnail exists, however it was not possible to access the video information. Please check your temporary directory permissions for read/write access by the webserver.',
  227. 'extractFrame_video_frame_count' => 'You have attempted to extract a thumbnail from a video but the thumbnail you are trying to extract does not exist in the video.',
  228. 'extractFrames_video_begin_frame_count' => 'You have attempted to extract thumbnails from a video but the thumbnail you are trying to start the extraction from does not exist in the video.',
  229. 'extractFrames_video_end_frame_count' => 'You have attempted to extract thumbnails from a video but the thumbnail you are trying to end the extraction at does not exist in the video.',
  230. 'setFormat_valid_format' => 'Value "#format" set from $toolkit->setFormat, is not a valid format. Valid values are PHPVIDEOTOOLKIT_FORMAT_3GP2, PHPVIDEOTOOLKIT_FORMAT_3GP, PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_AMR, PHPVIDEOTOOLKIT_FORMAT_ASF, PHPVIDEOTOOLKIT_FORMAT_AVI, PHPVIDEOTOOLKIT_FORMAT_FLV, PHPVIDEOTOOLKIT_FORMAT_GIF, PHPVIDEOTOOLKIT_FORMAT_MJ2, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG4, PHPVIDEOTOOLKIT_FORMAT_M4A, PHPVIDEOTOOLKIT_FORMAT_MPEG, PHPVIDEOTOOLKIT_FORMAT_MPEG1, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_MPEGVIDEO, PHPVIDEOTOOLKIT_FORMAT_PSP, PHPVIDEOTOOLKIT_FORMAT_RM, PHPVIDEOTOOLKIT_FORMAT_SWF, PHPVIDEOTOOLKIT_FORMAT_VOB, PHPVIDEOTOOLKIT_FORMAT_WAV, PHPVIDEOTOOLKIT_FORMAT_JPG. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-f" and $argument to your required value.',
  231. 'setAudioChannels_valid_channels' => 'Value "#channels" set from $toolkit->setAudioChannels, is not a valid integer. Valid values are 1, or 2. If you wish to specifically try to set another channels value you should use the advanced function $toolkit->addCommand. Set $command to "-ac" and $argument to your required value.',
  232. 'setAudioSampleFrequency_valid_frequency' => 'Value "#frequency" set from $toolkit->setAudioSampleFrequency, is not a valid integer. Valid values are 11025, 22050, 44100. If you wish to specifically try to set another frequency you should use the advanced function $toolkit->addCommand. Set $command to "-ar" and $argument to your required value.',
  233. 'setAudioFormat_valid_format' => 'Value "#format" set from $toolkit->setAudioFormat, is not a valid format. Valid values are PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_AMR, PHPVIDEOTOOLKIT_FORMAT_ASF, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_RM, PHPVIDEOTOOLKIT_FORMAT_WAV. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-acodec" and $argument to your required value.',
  234. 'setVideoFormat_valid_format' => 'Value "#format" set from $toolkit->setAudioFormat, is not a valid format. Valid values are PHPVIDEOTOOLKIT_FORMAT_3GP2, PHPVIDEOTOOLKIT_FORMAT_3GP, PHPVIDEOTOOLKIT_FORMAT_AVI, PHPVIDEOTOOLKIT_FORMAT_FLV, PHPVIDEOTOOLKIT_FORMAT_GIF, PHPVIDEOTOOLKIT_FORMAT_MJ2, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG4, PHPVIDEOTOOLKIT_FORMAT_M4A, PHPVIDEOTOOLKIT_FORMAT_MPEG, PHPVIDEOTOOLKIT_FORMAT_MPEG1, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_MPEGVIDEO. If you wish to specifically try to set another format you should use the advanced function $toolkit->addCommand. Set $command to "-vcodec" and $argument to your required value.',
  235. 'setAudioBitRate_valid_bitrate' => 'Value "#bitrate" set from $toolkit->setAudioBitRate, is not a valid integer. Valid values are 16, 32, 64, 128. If you wish to specifically try to set another bitrate you should use the advanced function $toolkit->addCommand. Set $command to "-ab" and $argument to your required value.',
  236. 'prepareImagesForConversionToVideo_one_img' => 'When compiling a movie from a series of images, you must include at least one image.',
  237. 'prepareImagesForConversionToVideo_img_404' => '"#img" does not exist.',
  238. 'prepareImagesForConversionToVideo_img_copy' => '"#img" can not be copied to "#tmpfile"',
  239. 'prepareImagesForConversionToVideo_img_type' => 'The images can not be prepared for conversion to video. Please make sure all images are of the same type, ie gif, png, jpeg and then try again.',
  240. 'setVideoOutputDimensions_valid_format' => 'Value "#format" set from $toolkit->setVideoOutputDimensions, is not a valid preset dimension. Valid values are PHPVIDEOTOOLKIT_SIZE_SQCIF, PHPVIDEOTOOLKIT_SIZE_SAS, PHPVIDEOTOOLKIT_SIZE_QCIF, PHPVIDEOTOOLKIT_SIZE_CIF, PHPVIDEOTOOLKIT_SIZE_4CIF, PHPVIDEOTOOLKIT_SIZE_QQVGA, PHPVIDEOTOOLKIT_SIZE_QVGA, PHPVIDEOTOOLKIT_SIZE_VGA, PHPVIDEOTOOLKIT_SIZE_SVGA, PHPVIDEOTOOLKIT_SIZE_XGA, PHPVIDEOTOOLKIT_SIZE_UXGA, PHPVIDEOTOOLKIT_SIZE_QXGA, PHPVIDEOTOOLKIT_SIZE_SXGA, PHPVIDEOTOOLKIT_SIZE_QSXGA, PHPVIDEOTOOLKIT_SIZE_HSXGA, PHPVIDEOTOOLKIT_SIZE_WVGA, PHPVIDEOTOOLKIT_SIZE_WXGA, PHPVIDEOTOOLKIT_SIZE_WSXGA, PHPVIDEOTOOLKIT_SIZE_WUXGA, PHPVIDEOTOOLKIT_SIZE_WOXGA, PHPVIDEOTOOLKIT_SIZE_WQSXGA, PHPVIDEOTOOLKIT_SIZE_WQUXGA, PHPVIDEOTOOLKIT_SIZE_WHSXGA, PHPVIDEOTOOLKIT_SIZE_WHUXGA, PHPVIDEOTOOLKIT_SIZE_CGA, PHPVIDEOTOOLKIT_SIZE_EGA, PHPVIDEOTOOLKIT_SIZE_HD480, PHPVIDEOTOOLKIT_SIZE_HD720, PHPVIDEOTOOLKIT_SIZE_HD1080. You can also manually set the width and height.',
  241. 'setVideoOutputDimensions_sas_dim' => 'It was not possible to determine the input video dimensions so it was not possible to continue. If you wish to override this error please change the call to setVideoOutputDimensions and add a true argument to the arguments list... setVideoOutputDimensions(PHPVIDEOTOOLKIT_SIZE_SAS, true);',
  242. 'setVideoOutputDimensions_valid_integer' => 'You tried to set the video output dimensions to an odd number. FFmpeg requires that the video output dimensions are of event value and divisible by 2. ie 2, 4, 6,... etc',
  243. 'setVideoAspectRatio_valid_ratio' => 'Value "#ratio" set from $toolkit->setVideoOutputDimensions, is not a valid preset dimension. Valid values are PHPVIDEOTOOLKIT_RATIO_STANDARD, PHPVIDEOTOOLKIT_RATIO_WIDE, PHPVIDEOTOOLKIT_RATIO_CINEMATIC. If you wish to specifically try to set another video aspect ratio you should use the advanced function $toolkit->addCommand. Set $command to "-aspect" and $argument to your required value.',
  244. 'addWatermark_img_404' => 'Watermark file "#watermark" does not exist.',
  245. 'addWatermark_vhook_disabled' => 'Vhooking is not enabled in your FFmpeg binary. In order to allow video watermarking you must have FFmpeg compiled with --enable-vhook set. You can however watermark any extracted images using GD. To enable frame watermarking, call $toolkit->addGDWatermark($file) before you execute the extraction.',
  246. 'addVideo_file_404' => 'File "#file" does not exist.',
  247. 'setOutput_output_dir_404' => 'Output directory "#dir" does not exist!',
  248. 'setOutput_output_dir_writable' => 'Output directory "#dir" is not writable!',
  249. 'setOutput_%_missing' => 'The output of this command will be images yet you have not included the "%index" or "%timecode" in the $output_name.',
  250. 'setOutput_%d_depreciated' => 'The use of %d in the output file name is now depreciated. Please use %index. Number padding is still supported. You may also use %timecode instead to add a timecode to the filename.',
  251. 'execute_input_404' => 'Execute error. Input file missing.',
  252. 'execute_output_not_set' => 'Execute error. Output not set.',
  253. 'execute_temp_unwritable' => 'Execute error. The tmp directory supplied is not writable.',
  254. 'execute_overwrite_process' => 'Execute error. A file exists in the temp directory and is of the same name as this process file. It will conflict with this conversion. Conversion stopped.',
  255. 'execute_overwrite_fail' => 'Execute error. Output file exists. Process halted. If you wish to automatically overwrite files set the third argument in "PHPVideoToolkit::setOutput();" to "PHPVIDEOTOOLKIT_OVERWRITE_EXISTING".',
  256. 'execute_partial_error' => 'Execute error. Output for file "#input" encountered a partial error. Files were generated, however one or more of them were empty.',
  257. 'execute_image_error' => 'Execute error. Output for file "#input" was not found. No images were generated.',
  258. 'execute_output_404' => 'Execute error. Output for file "#input" was not found. Please check server write permissions and/or available codecs compiled with FFmpeg. You can check the encode decode availability by inspecting the output array from PHPVideoToolkit::getFFmpegInfo().',
  259. 'execute_output_empty' => 'Execute error. Output for file "#input" was found, but the file contained no data. Please check the available codecs compiled with FFmpeg can support this type of conversion. You can check the encode decode availability by inspecting the output array from PHPVideoToolkit::getFFmpegInfo().',
  260. 'execute_image_file_exists' => 'Execute error. There is a file name conflict. The file "#file" already exists in the filesystem. If you wish to automatically overwrite files set the third argument in "PHPVideoToolkit::setOutput();" to "PHPVIDEOTOOLKIT_OVERWRITE_EXISTING".',
  261. 'execute_result_ok_but_unwritable' => 'Process Partially Completed. The process successfully completed however it was not possible to output to "#output". The output was left in the temp directory "#process" for a manual file movement.',
  262. 'execute_result_ok' => 'Process Completed. The process successfully completed. Output was generated to "#output".',
  263. 'ffmpeg_log_ffmpeg_output' => 'OUTPUT',
  264. 'ffmpeg_log_ffmpeg_result' => 'RESULT',
  265. 'ffmpeg_log_ffmpeg_command' => 'COMMAND',
  266. 'ffmpeg_log_ffmpeg_join_gunk' => 'FFMPEG JOIN OUTPUT',
  267. 'ffmpeg_log_ffmpeg_gunk' => 'FFMPEG OUTPUT',
  268. 'ffmpeg_log_separator' => '-------------------------------'
  269. );
  270. /**
  271. * A public var that is to the information available about
  272. * the current ffmpeg compiled binary.
  273. * @var mixed
  274. * @access public
  275. */
  276. var $ffmpeg_info = false;
  277. /**
  278. * A public var that determines if the ffmpeg binary has been found. The default value
  279. * is null unless getFFmpegInfo is called whereby depending on the results it is set to
  280. * true or false
  281. * @var mixed
  282. * @access public
  283. */
  284. var $ffmpeg_found = null;
  285. /**
  286. * A private var that contains the info of any file that is accessed by PHPVideoToolkit::getFileInfo();
  287. * @var array
  288. * @access private
  289. */
  290. var $_file_info = array();
  291. /**
  292. * Determines what happens when an error occurs
  293. * @var boolean If true then the script will die, if not false is return by the error
  294. * @access public
  295. */
  296. var $on_error_die = false;
  297. /**
  298. * Holds the log file name
  299. * @var string
  300. * @access private
  301. */
  302. var $_log_file = null;
  303. /**
  304. * Determines if when outputting image frames if the outputted files should have the %d number
  305. * replaced with the frames timecode.
  306. * @var boolean If true then the files will be renamed.
  307. * @access public
  308. */
  309. var $image_output_timecode = true;
  310. /**
  311. * Holds the timecode separator for when using $image_output_timecode = true
  312. * Not all systems allow ':' in filenames.
  313. * @var string
  314. * @access public
  315. */
  316. var $timecode_seperator_output = '-';
  317. /**
  318. * Holds the starting time code when outputting image frames.
  319. * @var string The timecode hh(n):mm:ss:ff
  320. * @access private
  321. */
  322. var $_image_output_timecode_start = '00:00:00.00';
  323. /**
  324. * The format in which the image %timecode placeholder string is outputted.
  325. * - %hh (hours) representative of hours
  326. * - %mm (minutes) representative of minutes
  327. * - %ss (seconds) representative of seconds
  328. * - %fn (frame number) representative of frames (of the current second, not total frames)
  329. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  330. * - %ft (frames total) representative of total frames (ie frame number)
  331. * - %st (seconds total) representative of total seconds (rounded).
  332. * - %sf (seconds floored) representative of total seconds (floored).
  333. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  334. * NOTE; there are special characters that will be replace by PHPVideoToolkit::$timecode_seperator_output, these characters are
  335. * - :
  336. * - .
  337. * @var string
  338. * @access public
  339. */
  340. var $image_output_timecode_format = '%hh-%mm-%ss-%fn';
  341. /**
  342. * Holds the fps of image extracts
  343. * @var integer
  344. * @access private
  345. */
  346. var $_image_output_timecode_fps = 1;
  347. /**
  348. * Holds the current execute commands that will need to be combined
  349. * @var array
  350. * @access private
  351. */
  352. var $_commands = array();
  353. /**
  354. * Holds the commands executed
  355. * @var array
  356. * @access private
  357. */
  358. var $_processed = array();
  359. /**
  360. * Holds the file references to those that have been processed
  361. * @var array
  362. * @access private
  363. */
  364. var $_files = array();
  365. /**
  366. * Holds the errors encountered
  367. * @access private
  368. * @var array
  369. */
  370. var $_errors = array();
  371. /**
  372. * Holds the input file / input file sequence
  373. * @access private
  374. * @var string
  375. */
  376. var $_input_file = null;
  377. /**
  378. * Holds the output file / output file sequence
  379. * @access private
  380. * @var string
  381. */
  382. var $_output_address = null;
  383. /**
  384. * Holds the process file / process file sequence
  385. * @access private
  386. * @var string
  387. */
  388. var $_process_address = null;
  389. /**
  390. * Temporary filename prefix
  391. * @access private
  392. * @var string
  393. */
  394. var $_tmp_file_prefix = 'tmp_';
  395. /**
  396. * Holds the temporary directory name
  397. * @access private
  398. * @var string
  399. */
  400. var $_tmp_directory = null;
  401. /**
  402. * Holds the directory paths that need to be removed by the ___destruct function
  403. * @access private
  404. * @var array
  405. */
  406. var $_unlink_dirs = array();
  407. /**
  408. * Holds the file paths that need to be deleted by the ___destruct function
  409. * @access private
  410. * @var array
  411. */
  412. var $_unlink_files = array();
  413. /**
  414. * Holds the timer start micro-float.
  415. * @access private
  416. * @var integer
  417. */
  418. var $_timer_start = 0;
  419. /**
  420. * Holds the times taken to process each file.
  421. * @access private
  422. * @var array
  423. */
  424. var $_timers = array();
  425. /**
  426. * Holds the times taken to process each file.
  427. * @access private
  428. * @var constant
  429. */
  430. var $_overwrite_mode = null;
  431. /**
  432. * Holds a integer value that flags if the image extraction is just a single frame.
  433. * @access private
  434. * @var integer
  435. */
  436. var $_single_frame_extraction = null;
  437. /**
  438. * Holds the watermark file that is used to watermark any outputted images via GD.
  439. * @access private
  440. * @var string
  441. */
  442. var $_watermark_url = null;
  443. /**
  444. * Holds the watermark options used to watermark any outputted images via GD.
  445. * @access private
  446. * @var array
  447. */
  448. var $_watermark_options = null;
  449. /**
  450. * Holds the number of files processed per run.
  451. * @access private
  452. * @var integer
  453. */
  454. var $_process_file_count = 0;
  455. /**
  456. * Holds the times taken to process each file.
  457. * @access private
  458. * @var array
  459. */
  460. var $_post_processes = array();
  461. /**
  462. * Holds commands should be sent added to the exec before the input file, this is by no means a definitive list
  463. * of all the ffmpeg commands, as it only utilizes the ones in use by this class. Also only commands that have
  464. * specific required places are entered in the arrays below. Anything not in these arrays will be treated as an
  465. * after-input item.
  466. * @access private
  467. * @var array
  468. */
  469. // var $_cmds_before_input = array();
  470. var $_cmds_before_input = array('-inputr');
  471. // var $_cmds_before_input = array('-r', '-f');
  472. /**
  473. * Constructs the class and sets the temporary directory.
  474. *
  475. * @access private
  476. * @param string $tmp_directory A full absolute path to you temporary directory
  477. */
  478. function PHPVideoToolkit($tmp_directory='/tmp/')
  479. {
  480. $this->_tmp_directory = $tmp_directory;
  481. // emulate php5 destructors
  482. register_shutdown_function(array(&$this, '__destruct'));
  483. }
  484. function microtimeFloat()
  485. {
  486. list($usec, $sec) = explode(" ", microtime());
  487. return ((float) $usec + (float) $sec);
  488. }
  489. /**
  490. * Resets the class
  491. *
  492. * @access public
  493. * @param boolean $keep_input_file Determines whether or not to reset the input file currently set.
  494. */
  495. function reset($keep_input_file=false, $keep_processes=false)
  496. {
  497. if($keep_input_file === false)
  498. {
  499. $this->_input_file = null;
  500. }
  501. if($keep_processes === false)
  502. {
  503. $this->_post_processes = array();
  504. }
  505. $this->_single_frame_extraction = null;
  506. $this->_output_address = null;
  507. $this->_process_address = null;
  508. $this->_log_file = null;
  509. $this->_commands = array();
  510. $this->_timer_start = 0;
  511. $this->_process_file_count = 0;
  512. $this->__destruct();
  513. }
  514. /**
  515. * Returns information about the specified file without having to use ffmpeg-php
  516. * as it consults the ffmpeg binary directly.
  517. *
  518. * @access public
  519. * @param string $file The absolute path of the file that is required to be manipulated.
  520. * @return mixed false on error encountered, true otherwise
  521. **/
  522. function getFFmpegInfo()
  523. {
  524. // check to see if this is a static call
  525. if(!$this)
  526. {
  527. $toolkit = new PHPVideoToolkit($tmp_directory);
  528. return $toolkit->getFFmpegInfo();
  529. }
  530. // check to see if the info has already been cached
  531. if($this->ffmpeg_info !== false)
  532. {
  533. return $this->ffmpeg_info;
  534. }
  535. // check to see if this is a static call
  536. $format = '';
  537. $data = array();
  538. // execute the ffmpeg lookup
  539. exec(PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -formats 2>&1', $buffer);
  540. $this->ffmpeg_found = $data['ffmpeg-found'] = !(strpos($buffer[0], 'command not found') !== false || strpos($buffer[0], 'No such file or directory') !== false);
  541. $data['binary'] = array();
  542. $data['compiler'] = array();
  543. $data['ffmpeg-php-support'] = $this->hasFFmpegPHPSupport();
  544. $data['raw'] = implode("\r\n", $buffer);
  545. if(!$this->ffmpeg_found)
  546. {
  547. $this->ffmpeg_info = $data;
  548. return $data;
  549. }
  550. $buffer = $data['raw'];
  551. // start building the info array
  552. $look_ups = array('configuration'=>'configuration: ', 'formats'=>'File formats:', 'codecs'=>'Codecs:', 'filters'=>'Bitstream filters:', 'protocols'=>'Supported file protocols:', 'abbreviations'=>'Frame size, frame rate abbreviations:', 'Note:');
  553. $total_lookups = count($look_ups);
  554. $pregs = array();
  555. $indexs = array();
  556. foreach($look_ups as $key=>$reg)
  557. {
  558. if(strpos($buffer, $reg) !== false)
  559. {
  560. $index = array_push($pregs, $reg);
  561. $indexs[$key] = $index;
  562. }
  563. }
  564. preg_match('/'.implode('(.*)', $pregs).'(.*)/s', $buffer, $matches);
  565. $configuration = trim($matches[$indexs['configuration']]);
  566. // grab the ffmpeg configuration flags
  567. preg_match_all('/--[a-zA-Z0-9\-]+/', $configuration, $config_flags);
  568. $data['binary']['configuration'] = $config_flags[0];
  569. $data['binary']['vhook-support'] = in_array('--enable-vhook', $config_flags[0]) || !in_array('--disable-vhook', $config_flags[0]);
  570. // grab the versions
  571. $data['binary']['versions'] = array();
  572. preg_match_all('/([a-zA-Z0-9\-]+) version: ([0-9\.]+)/', $configuration, $versions);
  573. for($i=0, $a=count($versions[0]); $i<$a; $i++)
  574. {
  575. $data['binary']['versions'][strtolower(trim($versions[1][$i]))] = $versions[2][$i];
  576. }
  577. // grab the ffmpeg compile info
  578. preg_match('/built on (.*), gcc: (.*)/', $configuration, $conf);
  579. if(count($conf) > 0)
  580. {
  581. $data['compiler']['gcc'] = $conf[2];
  582. $data['compiler']['build_date'] = $conf[1];
  583. $data['compiler']['build_date_timestamp'] = strtotime($conf[1]);
  584. }
  585. // grab the file formats available to ffmpeg
  586. preg_match_all('/ (DE|D|E) (.*) {1,} (.*)/', trim($matches[$indexs['formats']]), $formats);
  587. $data['formats'] = array();
  588. // loop and clean
  589. for($i=0, $a=count($formats[0]); $i<$a; $i++)
  590. {
  591. $data['formats'][strtolower(trim($formats[2][$i]))] = array(
  592. 'encode' => $formats[1][$i] == 'DE' || $formats[1][$i] == 'E',
  593. 'decode' => $formats[1][$i] == 'DE' || $formats[1][$i] == 'D',
  594. 'fullname' => $formats[3][$i]
  595. );
  596. }
  597. // grab the bitstream filters available to ffmpeg
  598. $data['filters'] = array();
  599. if(isset($indexs['filters']) && isset($matches[$indexs['filters']]))
  600. {
  601. $filters = trim($matches[$indexs['filters']]);
  602. if(empty($filters) === false)
  603. {
  604. $data['filters'] = explode(' ', $filters);
  605. }
  606. }
  607. // grab the file prototcols available to ffmpeg
  608. $data['filters'] = array();
  609. if(isset($indexs['protocols']) && isset($matches[$indexs['protocols']]))
  610. {
  611. $protocols = trim($matches[$indexs['protocols']]);
  612. if(empty($protocols) === false)
  613. {
  614. $data['protocols'] = explode(' ', str_replace(':', '', $protocols));
  615. }
  616. }
  617. // grab the abbreviations available to ffmpeg
  618. $data['abbreviations'] = array();
  619. if(isset($indexs['abbreviations']) && isset($matches[$indexs['abbreviations']]))
  620. {
  621. $abbreviations = trim($matches[$indexs['abbreviations']]);
  622. if(empty($abbreviations) === false)
  623. {
  624. $data['abbreviations'] = explode(' ', $abbreviations);
  625. }
  626. }
  627. $this->ffmpeg_info = $data;
  628. return $data;
  629. }
  630. /**
  631. * Determines the type of support that exists for the FFmpeg-PHP module.
  632. *
  633. * @access public
  634. * @return mixed. Boolean false if there is no support, String 'module' if the actuall
  635. * FFmpeg-PHP module is loaded, or String 'emulated' if the FFmpeg-PHP classes
  636. * can be emulated through the adapter classes.
  637. */
  638. function hasFFmpegPHPSupport()
  639. {
  640. return $this->ffmpeg_found === false ? false : (extension_loaded('ffmpeg') ? 'module' : (is_file(dirname(__FILE__).DS.'adapters'.DS.'ffmpeg-php'.DS.'ffmpeg_movie.php') && is_file(dirname(__FILE__).DS.'adapters'.DS.'ffmpeg-php'.DS.'ffmpeg_frame.php') && is_file(dirname(__FILE__).DS.'adapters'.DS.'ffmpeg-php'.DS.'ffmpeg_animated_gif.php') ? 'emulated' : false));
  641. }
  642. /**
  643. * Determines if the ffmpeg binary has been compiled with vhook support.
  644. *
  645. * @access public
  646. * @return mixed. Boolean false if there is no support, true there is support.
  647. */
  648. function hasVHookSupport()
  649. {
  650. $info = $this->getFFmpegInfo();
  651. return $info['binary']['vhook-support'];
  652. }
  653. /**
  654. * Returns information about the specified file without having to use ffmpeg-php
  655. * as it consults the ffmpeg binary directly. This idea for this function has been borrowed from
  656. * a French ffmpeg class located: http://www.phpcs.com/codesource.aspx?ID=45279
  657. *
  658. * @access public
  659. * @todo Change the search from string explode to a regex based search
  660. * @param string $file The absolute path of the file that is required to be manipulated.
  661. * @return mixed false on error encountered, true otherwise
  662. **/
  663. function getFileInfo($file=false, $tmp_directory='/tmp/')
  664. {
  665. // check to see if this is a static call
  666. if($file !== false && !$this)
  667. {
  668. $toolkit = new PHPVideoToolkit($tmp_directory);
  669. return $toolkit->getFileInfo($file);
  670. }
  671. // if the file has not been specified check to see if an input file has been specified
  672. if($file === false)
  673. {
  674. if(!$this->_input_file)
  675. {
  676. // input file not valid
  677. return $this->_raiseError('getFileInfo_no_input');
  678. //<- exits
  679. }
  680. $file = $this->_input_file;
  681. }
  682. $file = escapeshellarg($file);
  683. // create a hash of the filename
  684. $hash = md5($file);
  685. // check to see if the info has already been generated
  686. if(isset($this->_file_info[$hash]))
  687. {
  688. return $this->_file_info[$hash];
  689. }
  690. // execute the ffmpeg lookup
  691. exec(PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$file.' 2>&1', $buffer);
  692. $buffer = implode("\r\n", $buffer);
  693. $data = array();
  694. // grab the duration and bitrate data
  695. preg_match_all('/Duration: (.*)/', $buffer, $matches);
  696. if(count($matches) > 0)
  697. {
  698. $line = trim($matches[0][0]);
  699. // capture any data
  700. preg_match_all('/(Duration|start|bitrate): ([^,]*)/', $line, $matches);
  701. // setup the default data
  702. $data['duration'] = array(
  703. 'timecode' => array(
  704. 'seconds' => array(
  705. 'exact' => -1,
  706. 'excess' => -1
  707. ),
  708. 'rounded' => -1,
  709. )
  710. );
  711. // get the data
  712. foreach ($matches[1] as $key => $detail)
  713. {
  714. $value = $matches[2][$key];
  715. switch(strtolower($detail))
  716. {
  717. case 'duration' :
  718. // print_r($value);
  719. $data['duration']['timecode']['rounded'] = substr($value, 0, 8);
  720. $data['duration']['timecode']['frames'] = array();
  721. $data['duration']['timecode']['frames']['exact'] = $value;
  722. $data['duration']['timecode']['frames']['excess'] = intval(substr($value, 9));
  723. break;
  724. case 'bitrate' :
  725. $data['bitrate'] = strtoupper($value) === 'N/A' ? -1 : intval($value);
  726. break;
  727. case 'start' :
  728. $data['duration']['start'] = $value;
  729. break;
  730. }
  731. }
  732. }
  733. // match the video stream info
  734. preg_match('/Stream(.*): Video: (.*)/', $buffer, $matches);
  735. if(count($matches) > 0)
  736. {
  737. $data['video'] = array();
  738. // get the dimension parts
  739. preg_match('/([0-9]{1,5})x([0-9]{1,5})/', $matches[2], $dimensions_matches);
  740. // print_r($dimensions_matches);
  741. $dimensions_value = $dimensions_matches[0];
  742. $data['video']['dimensions'] = array(
  743. 'width' => floatval($dimensions_matches[1]),
  744. 'height' => floatval($dimensions_matches[2])
  745. );
  746. // get the framerate
  747. preg_match('/([0-9\.]+) (fps|tb)\(r\)/', $matches[0], $fps_matches);
  748. $data['duration']['timecode']['frames']['frame_rate'] = $data['video']['frame_rate'] = floatval($fps_matches[1]);
  749. $data['duration']['timecode']['seconds']['total'] = $data['duration']['seconds'] = $this->formatTimecode($data['duration']['timecode']['frames']['exact'], '%hh:%mm:%ss.%fn', '%st.%ms', $data['video']['frame_rate']);
  750. $fps_value = $fps_matches[0];
  751. // get the ratios
  752. preg_match('/\[PAR ([0-9\:\.]+) DAR ([0-9\:\.]+)\]/', $matches[0], $ratio_matches);
  753. if(count($ratio_matches))
  754. {
  755. $data['video']['pixel_aspect_ratio'] = $ratio_matches[1];
  756. $data['video']['display_aspect_ratio'] = $ratio_matches[2];
  757. }
  758. // work out the number of frames
  759. if(isset($data['duration']) && isset($data['video']))
  760. {
  761. // set the total frame count for the video
  762. $data['video']['frame_count'] = ceil($data['duration']['seconds'] * $data['video']['frame_rate']);
  763. // set the framecode
  764. $data['duration']['timecode']['seconds']['excess'] = floatval($data['duration']['seconds']) - floor($data['duration']['seconds']);
  765. $data['duration']['timecode']['seconds']['exact'] = $this->formatSeconds($data['duration']['seconds'], '%hh:%mm:%ss.%ms');
  766. $data['duration']['timecode']['frames']['total'] = $data['video']['frame_count'];
  767. }
  768. // formats should be anything left over, let me know if anything else exists
  769. $parts = explode(',', $matches[2]);
  770. $other_parts = array($dimensions_value, $fps_value);
  771. $formats = array();
  772. foreach($parts as $key=>$part)
  773. {
  774. $part = trim($part);
  775. if(!in_array($part, $other_parts))
  776. {
  777. array_push($formats, $part);
  778. }
  779. }
  780. $data['video']['pixel_format'] = $formats[1];
  781. $data['video']['codec'] = $formats[0];
  782. }
  783. // match the audio stream info
  784. preg_match('/Stream(.*): Audio: (.*)/', $buffer, $matches);
  785. if(count($matches) > 0)
  786. {
  787. // setup audio values
  788. $data['audio'] = array(
  789. 'stereo' => -1,
  790. 'sample_rate' => -1,
  791. 'sample_rate' => -1
  792. );
  793. $other_parts = array();
  794. // get the stereo value
  795. preg_match('/(stereo|mono)/i', $matches[0], $stereo_matches);
  796. if(count($stereo_matches))
  797. {
  798. $data['audio']['stereo'] = $stereo_matches[0];
  799. array_push($other_parts, $stereo_matches[0]);
  800. }
  801. // get the sample_rate
  802. preg_match('/([0-9]{3,6}) Hz/', $matches[0], $sample_matches);
  803. if(count($sample_matches))
  804. {
  805. $data['audio']['sample_rate'] = count($sample_matches) ? floatval($sample_matches[1]) : -1;
  806. array_push($other_parts, $sample_matches[0]);
  807. }
  808. // get the bit rate
  809. preg_match('/([0-9]{1,3}) kb\/s/', $matches[0], $bitrate_matches);
  810. if(count($bitrate_matches))
  811. {
  812. $data['audio']['bitrate'] = count($bitrate_matches) ? floatval($bitrate_matches[1]) : -1;
  813. array_push($other_parts, $bitrate_matches[0]);
  814. }
  815. // formats should be anything left over, let me know if anything else exists
  816. $parts = explode(',', $matches[2]);
  817. $formats = array();
  818. foreach($parts as $key=>$part)
  819. {
  820. $part = trim($part);
  821. if(!in_array($part, $other_parts))
  822. {
  823. array_push($formats, $part);
  824. }
  825. }
  826. $data['audio']['codec'] = $formats[0];
  827. // if no video is set then no audio frame rate is set
  828. if($data['duration']['timecode']['seconds']['exact'] === -1)
  829. {
  830. $exact_timecode = $this->formatTimecode($data['duration']['timecode']['frames']['exact'], '%hh:%mm:%ss.%fn', '%hh:%mm:%ss.%ms', 1000);
  831. $data['duration']['timecode']['seconds'] = array(
  832. 'exact' => $exact_timecode,
  833. 'excess' => intval(substr($exact_timecode, 8)),
  834. 'total' => $this->formatTimecode($data['duration']['timecode']['frames']['exact'], '%hh:%mm:%ss.%fn', '%ss.%ms', 1000)
  835. );
  836. $data['duration']['timecode']['frames']['frame_rate'] = 1000;
  837. $data['duration']['seconds'] = $data['duration']['timecode']['seconds']['total'];
  838. //$this->formatTimecode($data['duration']['timecode']['frames']['exact'], '%hh:%mm:%ss.%fn', '%st.%ms', $data['video']['frame_rate']);
  839. }
  840. }
  841. // check that some data has been obtained
  842. if(!count($data))
  843. {
  844. $data = false;
  845. }
  846. else
  847. {
  848. $data['_raw_info'] = $buffer;
  849. }
  850. return $this->_file_info[$hash] = $data;
  851. }
  852. /**
  853. * Determines if the input media has a video stream.
  854. *
  855. * @access public
  856. * @param string $file The absolute path of the file that is required to be manipulated.
  857. * @return bool
  858. **/
  859. function fileHasVideo($file=false)
  860. {
  861. // check to see if this is a static call
  862. if($file !== false && !isset($this))
  863. {
  864. $toolkit = new PHPVideoToolkit();
  865. $data = $toolkit->getFileInfo($file);
  866. }
  867. // if the file has not been specified check to see if an input file has been specified
  868. else if($file === false)
  869. {
  870. if(!$this->_input_file)
  871. {
  872. // input file not valid
  873. return $this->_raiseError('inputFileHasVideo_no_input');
  874. //<- exits
  875. }
  876. $file = $this->_input_file;
  877. $data = $this->getFileInfo($file);
  878. }
  879. return isset($data['video']);
  880. }
  881. /**
  882. * Determines if the input media has an audio stream.
  883. *
  884. * @access public
  885. * @param string $file The absolute path of the file that is required to be manipulated.
  886. * @return bool
  887. **/
  888. function fileHasAudio($file=false)
  889. {
  890. // check to see if this is a static call
  891. if($file !== false && !isset($this))
  892. {
  893. $toolkit = new PHPVideoToolkit();
  894. $data = $toolkit->getFileInfo($file);
  895. }
  896. // if the file has not been specified check to see if an input file has been specified
  897. else if($file === false)
  898. {
  899. if(!$this->_input_file)
  900. {
  901. // input file not valid
  902. return $this->_raiseError('inputFileHasAudio_no_input');
  903. //<- exits
  904. }
  905. $file = $this->_input_file;
  906. $data = $this->getFileInfo($file);
  907. }
  908. return isset($data['audio']);
  909. }
  910. /**
  911. * Determines if your ffmpeg has particular codec support for encode or decode.
  912. *
  913. * @access public
  914. * @param string $codec The name of the codec you are checking for.
  915. * @param const $support PHPVideoToolkit::ENCODE or PHPVideoToolkit::DECODE, depending on which functionality is desired.
  916. * @return mixed. Boolean false if there is no support, true if there is support.
  917. */
  918. function hasCodecSupport($codec, $support=PHPVIDEOTOOLKIT_ENCODE)
  919. {
  920. $codec = strtolower($codec);
  921. $data = $this->getFFmpegInfo();
  922. return isset($data['formats'][$codec]) ? $data['formats'][$codec][$support] : false;
  923. }
  924. /**
  925. * Sets the input file that is going to be manipulated.
  926. *
  927. * @access public
  928. * @param string $file The absolute path of the file that is required to be manipulated.
  929. * @param mixed $input_frame_rate If 0 (default) then no input frame rate is set, if false it is automatically retreived, otherwise
  930. * any other integer will be set as the incoming frame rate.
  931. * @return boolean false on error encountered, true otherwise
  932. */
  933. function setInputFile($file, $input_frame_rate=0)
  934. {
  935. $files_length = count($file);
  936. // if the total number of files entered is 1 then only one file is being processed
  937. if($files_length == 1)
  938. {
  939. // check the input file, if there is a %d in there or a similar %03d then the file inputted is a sequence, if neither of those is found
  940. // then qheck to see if the file exists
  941. if(!preg_match('/\%([0-9]+)d/', $file) && strpos($file, '%d') === false && !is_file($file))
  942. {
  943. // input file not valid
  944. return $this->_raiseError('setInputFile_file_existence', array('file'=>$file));
  945. //<- exits
  946. }
  947. $escaped_name = $file;
  948. // $escaped_name = escapeshellarg($files[0]);
  949. $this->_input_file = $escaped_name;
  950. $this->_input_file_id = md5($escaped_name);
  951. // the -inputr is a hack for -r to come before the input
  952. if($input_frame_rate !== 0)
  953. {
  954. $info = $this->getFileInfo();
  955. if(isset($info['video']))
  956. {
  957. if($input_frame_rate === false)
  958. {
  959. $input_frame_rate = $info['video']['frame_rate'];
  960. }
  961. // input frame rate is a command hack
  962. $this->addCommand('-inputr', $input_frame_rate);
  963. }
  964. }
  965. }
  966. else
  967. {
  968. // more than one video is being added as input so we must join them all
  969. call_user_func_array(array(&$this, 'addVideo'), array($file, $input_frame_rate));
  970. }
  971. return true;
  972. }
  973. /**
  974. * A shortcut for converting video to FLV.
  975. *
  976. * @access public
  977. * @param integer $audio_sample_frequency
  978. * @param integer $audio_bitrate
  979. */
  980. function setFormatToFLV($audio_sample_frequency=44100, $audio_bitrate=64)
  981. {
  982. $this->addCommand('-sameq');
  983. $this->setAudioFormat(PHPVIDEOTOOLKIT_FORMAT_MP3);
  984. // adjust the audio rates
  985. $this->setAudioBitRate($audio_bitrate);
  986. $this->setAudioSampleFrequency($audio_sample_frequency);
  987. // set the video format
  988. $this->setFormat(PHPVIDEOTOOLKIT_FORMAT_FLV);
  989. // flag that the flv has to have meta data added after the excecution of this command
  990. // register the post tidy process
  991. $this->registerPostProcess('_addMetaToFLV', $this);
  992. }
  993. /**
  994. * When converting video to FLV the meta data has to be added by a ruby program called FLVTools2.
  995. * This is a second exec call only after the video has been converted to FLV
  996. * http://inlet-media.de/flvtool2
  997. *
  998. * @access private
  999. */
  1000. function _addMetaToFLV($files)
  1001. {
  1002. $file = array_pop($files);
  1003. // prepare the command suitable for exec
  1004. $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FLVTOOLS_BINARY, '-U '.$file);
  1005. // execute the command
  1006. exec($exec_string);
  1007. if(is_array($this->_processed[0]))
  1008. {
  1009. array_push($this->_processed[0], $exec_string);
  1010. }
  1011. else
  1012. {
  1013. $this->_processed[0] = array($this->_processed[0], $exec_string);
  1014. }
  1015. return true;
  1016. }
  1017. /**
  1018. * Streams a FLV file from a given point. You can control bandwidth, cache and session options.
  1019. * Inspired by xmoov-php
  1020. * @see xmoov-php,
  1021. * - @link http://xmoov.com/
  1022. * - @author Eric Lorenzo Benjamin jr
  1023. * @access public
  1024. * @param integer $seek_pos The position in the file to seek to.
  1025. * @param array|boolean $bandwidth_options If a boolean value, false then no bandwidth limiting will take place.
  1026. * If true then bandwidth limiting will take place with packet_size = 90 and packet_interval = 0.3.
  1027. * If an array the following values are default packet_size = 90 and packet_interval = 0.3, you will also
  1028. * have to set active = true, ie array('active'=>true, 'packet_size'=>90, 'packet_interval'=>0.3)
  1029. * @param boolean $allow_cache If true the file will be allowed to cache in the browser, if false then it won't
  1030. * @return boolean
  1031. */
  1032. function flvStreamSeek($seek_pos=0, $bandwidth_options=array(), $allow_cache=true)
  1033. {
  1034. // check for input file
  1035. if(!$this->_input_file)
  1036. {
  1037. // input file not valid
  1038. return $this->_raiseError('streamFLV_no_input');
  1039. //<- exits
  1040. }
  1041. // make the pos safe
  1042. $seek_pos = intval($seek_pos);
  1043. // absorb the bandwidth options
  1044. $bandwidth_options = is_array($bandwidth_options) ? array_merge(array('active'=>false, 'packet_size'=>90, 'packet_interval'=>0.3), $bandwidth_options) : array('active'=>$bandwidth_options, 'packet_size'=>90, 'packet_interval'=>0.3);
  1045. // if this file is not allowed to be cached send cache headers for all browsers.
  1046. if(!$allow_cache)
  1047. {
  1048. session_cache_limiter('nocache');
  1049. header('Expires: Thu, 19 Nov 1981 08:52:00 GMT');
  1050. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  1051. header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
  1052. header('Pragma: no-cache');
  1053. }
  1054. // open file
  1055. $handle = fopen($this->_input_file, 'rb');
  1056. $file_size = filesize($this->_input_file) - (($seek_pos > 0) ? $seek_pos + 1 : 0);
  1057. // send the flv headers
  1058. header('Content-Type: video/x-flv');
  1059. header('Content-Disposition: attachment; filename="'.basename($this->_input_file).'"');
  1060. header('Content-Length: '.$file_size);
  1061. // flv format header
  1062. if($seek_pos != 0)
  1063. {
  1064. print('FLV');
  1065. print(pack('C', 1));
  1066. print(pack('C', 1));
  1067. print(pack('N', 9));
  1068. print(pack('N', 9));
  1069. }
  1070. // seek to the required point
  1071. if(fseek($handle, $seek_pos) === -1)
  1072. {
  1073. // input file not valid
  1074. return $this->_raiseError('streamFLV_passed_eof');
  1075. //<- exits
  1076. }
  1077. // if bandwidth control is active then workout the options
  1078. if($bandwidth_options['active'])
  1079. {
  1080. $packet_interval = intval($bandwidth_options['packet_interval']);
  1081. $packet_size = intval($bandwidth_options['packet_size']) * 1042;
  1082. }
  1083. // output the file
  1084. while(!feof($handle))
  1085. {
  1086. // limit the bandwidth
  1087. if($bandwidth_options['active'] && $packet_interval > 0)
  1088. {
  1089. // output the required packet
  1090. $time_start = $this->microtimeFloat();
  1091. echo fread($handle, $packet_size);
  1092. $time_stop = $this->microtimeFloat();
  1093. // delay the output
  1094. $time_difference = $time_stop - $time_start;
  1095. if($time_difference < $packet_interval)
  1096. {
  1097. usleep(($packet_interval * 1000000) - ($time_difference * 1000000));
  1098. }
  1099. }
  1100. // no bandwidth limiting
  1101. else
  1102. {
  1103. echo fread($handle, $file_size);
  1104. }
  1105. }
  1106. // close the file
  1107. fclose($handle);
  1108. return true;
  1109. }
  1110. /**
  1111. * This is an alias for setFormat, but restricts it to audio only formats.
  1112. *
  1113. * @access public
  1114. * @param integer $format A supported audio format.
  1115. * @param integer $audio_sample_frequency
  1116. * @param integer $audio_bitrate
  1117. **/
  1118. function extractAudio($format=PHPVIDEOTOOLKIT_FORMAT_MP3, $audio_sample_frequency=44100, $audio_bitrate=64)
  1119. {
  1120. // check the format is one of the audio formats
  1121. if(!in_array($format, array(PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG4, PHPVIDEOTOOLKIT_FORMAT_M4A, PHPVIDEOTOOLKIT_FORMAT_WAV)))
  1122. {
  1123. return $this->_raiseError('extractAudio_valid_format', array('format'=>$format));
  1124. //<- exits
  1125. }
  1126. $this->setFormat($format);
  1127. // adjust the audio rates
  1128. $this->setAudioBitRate($audio_bitrate);
  1129. $this->setAudioSampleFrequency($audio_sample_frequency);
  1130. }
  1131. /**
  1132. * Sets the new video format.
  1133. *
  1134. * @access public
  1135. * @param defined $format The format should use one of the defined variables stated below.
  1136. * PHPVIDEOTOOLKIT_FORMAT_3GP2 - 3g2
  1137. * PHPVIDEOTOOLKIT_FORMAT_3GP - 3gp
  1138. * PHPVIDEOTOOLKIT_FORMAT_AAC - aac
  1139. * PHPVIDEOTOOLKIT_FORMAT_AIFF - aiff
  1140. * PHPVIDEOTOOLKIT_FORMAT_AMR - amr
  1141. * PHPVIDEOTOOLKIT_FORMAT_ASF - asf
  1142. * PHPVIDEOTOOLKIT_FORMAT_AVI - avi
  1143. * PHPVIDEOTOOLKIT_FORMAT_FLV - flv
  1144. * PHPVIDEOTOOLKIT_FORMAT_GIF - gif
  1145. * PHPVIDEOTOOLKIT_FORMAT_MJ2 - mj2
  1146. * PHPVIDEOTOOLKIT_FORMAT_MP2 - mp2
  1147. * PHPVIDEOTOOLKIT_FORMAT_MP3 - mp3
  1148. * PHPVIDEOTOOLKIT_FORMAT_MP4 - mp4
  1149. * PHPVIDEOTOOLKIT_FORMAT_MPEG4 - mpeg4
  1150. * PHPVIDEOTOOLKIT_FORMAT_M4A - m4a
  1151. * PHPVIDEOTOOLKIT_FORMAT_MPEG - mpeg
  1152. * PHPVIDEOTOOLKIT_FORMAT_MPEG1 - mpeg1video
  1153. * PHPVIDEOTOOLKIT_FORMAT_MPEG2 - mpeg2video
  1154. * PHPVIDEOTOOLKIT_FORMAT_MPEGVIDEO - mpegvideo
  1155. * PHPVIDEOTOOLKIT_FORMAT_PSP - psp
  1156. * PHPVIDEOTOOLKIT_FORMAT_RM - rm
  1157. * PHPVIDEOTOOLKIT_FORMAT_SWF - swf
  1158. * PHPVIDEOTOOLKIT_FORMAT_VOB - vob
  1159. * PHPVIDEOTOOLKIT_FORMAT_WAV - wav
  1160. * PHPVIDEOTOOLKIT_FORMAT_JPG - jpg
  1161. * @return boolean false on error encountered, true otherwise
  1162. */
  1163. function setFormat($format)
  1164. {
  1165. // validate input
  1166. if(!in_array($format, array(PHPVIDEOTOOLKIT_FORMAT_3GP2, PHPVIDEOTOOLKIT_FORMAT_3GP, PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_AMR, PHPVIDEOTOOLKIT_FORMAT_ASF, PHPVIDEOTOOLKIT_FORMAT_AVI, PHPVIDEOTOOLKIT_FORMAT_FLV, PHPVIDEOTOOLKIT_FORMAT_GIF, PHPVIDEOTOOLKIT_FORMAT_MJ2, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG4, PHPVIDEOTOOLKIT_FORMAT_M4A, PHPVIDEOTOOLKIT_FORMAT_MPEG, PHPVIDEOTOOLKIT_FORMAT_MPEG1, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_MPEGVIDEO, PHPVIDEOTOOLKIT_FORMAT_PSP, PHPVIDEOTOOLKIT_FORMAT_RM, PHPVIDEOTOOLKIT_FORMAT_SWF, PHPVIDEOTOOLKIT_FORMAT_VOB, PHPVIDEOTOOLKIT_FORMAT_WAV, PHPVIDEOTOOLKIT_FORMAT_JPG)))
  1167. {
  1168. return $this->_raiseError('setFormat_valid_format', array('format'=>$format));
  1169. //<- exits
  1170. }
  1171. return $this->addCommand('-f', $format);
  1172. }
  1173. /**
  1174. * Sets the audio sample frequency for audio outputs
  1175. *
  1176. * @access public
  1177. * @param integer $audio_sample_frequency Valid values are 11025, 22050, 44100
  1178. * @return boolean false on error encountered, true otherwise
  1179. */
  1180. function setAudioSampleFrequency($audio_sample_frequency)
  1181. {
  1182. // validate input
  1183. if(!in_array(intval($audio_sample_frequency), array(11025, 22050, 44100)))
  1184. {
  1185. return $this->_raiseError('setAudioSampleFrequency_valid_frequency', array('frequency'=>$audio_sample_frequency));
  1186. //<- exits
  1187. }
  1188. return $this->addCommand('-ar', $audio_sample_frequency);
  1189. }
  1190. /**
  1191. * @access public
  1192. * @depreciated
  1193. * @see PHPVideoToolkit::setAudioCodec()
  1194. */
  1195. function setAudioFormat($video_format)
  1196. {
  1197. return $this->setAudioCodec($video_format);
  1198. }
  1199. /**
  1200. * Sets the number of audio channels
  1201. *
  1202. * @access public
  1203. * @param integer $channel_type Valid values are PHPVIDEOTOOLKIT_AUDIO_MONO, PHPVIDEOTOOLKIT_AUDIO_STEREO
  1204. * @return boolean false on error encountered, true otherwise
  1205. */
  1206. function setAudioChannels($channel_type=PHPVIDEOTOOLKIT_AUDIO_MONO)
  1207. {
  1208. // validate input
  1209. if(!in_array($channel_type, array(PHPVIDEOTOOLKIT_AUDIO_MONO, PHPVIDEOTOOLKIT_AUDIO_STEREO)))
  1210. {
  1211. return $this->_raiseError('setAudioChannels_valid_channels', array('channels'=>$channel_type));
  1212. //<- exits
  1213. }
  1214. return $this->addCommand('-ac', $channel_type);
  1215. }
  1216. /**
  1217. * Sets the audio format for audio outputs
  1218. *
  1219. * @access public
  1220. * @param integer $audio_format Valid values are PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_AMR, PHPVIDEOTOOLKIT_FORMAT_ASF, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_RM, PHPVIDEOTOOLKIT_FORMAT_WAV
  1221. * @return boolean false on error encountered, true otherwise
  1222. */
  1223. function setAudioCodec($audio_format)
  1224. {
  1225. // validate input
  1226. if(!in_array($audio_format, array(PHPVIDEOTOOLKIT_FORMAT_AAC, PHPVIDEOTOOLKIT_FORMAT_AIFF, PHPVIDEOTOOLKIT_FORMAT_AMR, PHPVIDEOTOOLKIT_FORMAT_ASF, PHPVIDEOTOOLKIT_FORMAT_MP2, PHPVIDEOTOOLKIT_FORMAT_MP3, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_RM, PHPVIDEOTOOLKIT_FORMAT_WAV)))
  1227. {
  1228. return $this->_raiseError('setAudioFormat_valid_format', array('format'=>$audio_format));
  1229. //<- exits
  1230. }
  1231. // run a libmp3lame check as it require different mp3 codec
  1232. // updated thanks to Varon for providing the research
  1233. if($audio_format == PHPVIDEOTOOLKIT_FORMAT_MP3)
  1234. {
  1235. $info = $this->getFFmpegInfo();
  1236. if(isset($info['formats']['libmp3lame']))
  1237. {
  1238. $audio_format = 'libmp3lame';
  1239. }
  1240. }
  1241. return $this->addCommand('-acodec', $audio_format);
  1242. }
  1243. /**
  1244. * @access public
  1245. * @depreciated
  1246. * @see PHPVideoToolkit::setVideoCodec()
  1247. */
  1248. function setVideoFormat($video_format)
  1249. {
  1250. return $this->setVideoCodec($video_format);
  1251. }
  1252. /**
  1253. * Sets the video format for video outputs. This should not be confused with setFormat. setVideoFormat does not generally need to
  1254. * be called unless setting a specific video format for a type of media format. It gets a little confusing...
  1255. *
  1256. * @access public
  1257. * @param integer $video_format Valid values are 11025, 22050, 44100
  1258. * @return boolean false on error encountered, true otherwise
  1259. */
  1260. function setVideoCodec($video_codec)
  1261. {
  1262. // validate input
  1263. if(!in_array($video_codec, array(PHPVIDEOTOOLKIT_FORMAT_3GP2, PHPVIDEOTOOLKIT_FORMAT_3GP, PHPVIDEOTOOLKIT_FORMAT_AVI, PHPVIDEOTOOLKIT_FORMAT_FLV, PHPVIDEOTOOLKIT_FORMAT_GIF, PHPVIDEOTOOLKIT_FORMAT_MJ2, PHPVIDEOTOOLKIT_FORMAT_MP4, PHPVIDEOTOOLKIT_FORMAT_MPEG4, PHPVIDEOTOOLKIT_FORMAT_M4A, PHPVIDEOTOOLKIT_FORMAT_MPEG, PHPVIDEOTOOLKIT_FORMAT_MPEG1, PHPVIDEOTOOLKIT_FORMAT_MPEG2, PHPVIDEOTOOLKIT_FORMAT_MPEGVIDEO)))
  1264. {
  1265. return $this->_raiseError('setVideoFormat_valid_format', array('format'=>$video_codec));
  1266. //<- exits
  1267. }
  1268. return $this->addCommand('-vcodec', $video_codec);
  1269. }
  1270. /**
  1271. * Disables audio encoding
  1272. *
  1273. * @access public
  1274. * @return boolean false on error encountered, true otherwise
  1275. */
  1276. function disableAudio()
  1277. {
  1278. return $this->addCommand('-an');
  1279. }
  1280. /**
  1281. * Disables video encoding
  1282. *
  1283. * @access public
  1284. * @return boolean false on error encountered, true otherwise
  1285. */
  1286. public function disableVideo()
  1287. {
  1288. return $this->addCommand('-vn');
  1289. }
  1290. /**
  1291. * Sets the audio bitrate
  1292. *
  1293. * @access public
  1294. * @param integer $audio_bitrate Valid values are 16, 32, 64
  1295. * @return boolean false on error encountered, true otherwise
  1296. */
  1297. function setAudioBitRate($bitrate)
  1298. {
  1299. // validate input
  1300. if(!in_array(intval($bitrate), array(16, 32, 64, 128)))
  1301. {
  1302. return $this->_raiseError('setAudioBitRate_valid_bitrate', array('bitrate'=>$bitrate));
  1303. //<- exits
  1304. }
  1305. return $this->addCommand('-ab', $bitrate.'kb');
  1306. }
  1307. /**
  1308. * Compiles an array of images into a video. This sets the input file (setInputFile) so you do not need to set it.
  1309. * The images should be a full absolute path to the actual image file.
  1310. * NOTE 1; This copies and renames all the supplied images into a temporary folder so the images don't have to be specifically named. However, when
  1311. * creating the ffmpeg instance you will need to set the absolute path to the temporary folder. The default path is '/tmp/'.
  1312. * NOTE 2; Please make sure all of the images are all of the same type.
  1313. *
  1314. * @access public
  1315. * @param array $images An array of images that are to be joined and converted into a video
  1316. * @param integer $input_frame_rate An integer that will specify the input frame rate for the images.
  1317. * @return boolean Returns false on encountering an error
  1318. */
  1319. function prepareImagesForConversionToVideo($images, $input_frame_rate)
  1320. {
  1321. // http://ffmpeg.mplayerhq.hu/faq.html#TOC3
  1322. // ffmpeg -f image2 -i img%d.jpg /tmp/a.mpg
  1323. if(empty($images))
  1324. {
  1325. return $this->_raiseError('prepareImagesForConversionToVideo_one_img');
  1326. //<- exits
  1327. }
  1328. // loop through and validate existence first before making a temporary copy
  1329. foreach ($images as $key=>$img)
  1330. {
  1331. if(!is_file($img))
  1332. {
  1333. return $this->_raiseError('prepareImagesForConversionToVideo_img_404', array('img'=>$img));
  1334. //<- exits
  1335. }
  1336. }
  1337. if(!is_dir($this->_tmp_directory))
  1338. {
  1339. return $this->_raiseError('generic_temp_404');
  1340. //<- exits
  1341. }
  1342. if(!is_writable($this->_tmp_directory))
  1343. {
  1344. return $this->_raiseError('generic_temp_writable');
  1345. //<- exits
  1346. }
  1347. // get the number of preceding places for the files based on how many files there are to copy
  1348. $total = count($images);
  1349. // create a temp dir in the temp dir
  1350. $uniqid = $this->unique();
  1351. mkdir($this->_tmp_directory.$uniqid, 0777);
  1352. // loop through, copy and rename specified images to the temp dir
  1353. $ext = false;
  1354. foreach ($images as $key=>$img)
  1355. {
  1356. $file_ext = array_pop(explode('.', $img));
  1357. if($ext !== false && $ext !== $file_ext)
  1358. {
  1359. return $this->_raiseError('prepareImagesForConversionToVideo_img_type');
  1360. //<- exits
  1361. }
  1362. $ext = $file_ext;
  1363. $tmp_file = $this->_tmp_directory.$uniqid.DS.$this->_tmp_file_prefix.$key.'.'.$ext;
  1364. if(!@copy($img, $tmp_file))
  1365. {
  1366. return $this->_raiseError('prepareImagesForConversionToVideo_img_copy', array('img'=>$img, 'tmpfile'=>$tmp_file));
  1367. //<- exits
  1368. }
  1369. // push the tmp file name into the unlinks so they can be deleted on class destruction
  1370. array_push($this->_unlink_files, $tmp_file);
  1371. }
  1372. // the inputr is a hack for -r to come before the input
  1373. $this->addCommand('-inputr', $input_frame_rate);
  1374. // exit;
  1375. // add the directory to the unlinks
  1376. array_push($this->_unlink_dirs, $this->_tmp_directory.$uniqid);
  1377. // get the input file format
  1378. $file_iteration = $this->_tmp_file_prefix.'%d.'.$ext;
  1379. // set the input filename
  1380. return $this->setInputFile($this->_tmp_directory.$uniqid.DS.$file_iteration);
  1381. }
  1382. /**
  1383. * Sets the video bitrate
  1384. *
  1385. * @access public
  1386. * @param integer $bitrate
  1387. * @return boolean
  1388. */
  1389. function setVideoBitRate($bitrate)
  1390. {
  1391. $bitrate = intval($bitrate);
  1392. return $this->addCommand('-b', $bitrate.'kb');
  1393. }
  1394. /**
  1395. * Sets the amount of time an animated gif output will loop
  1396. *
  1397. * @access public
  1398. * @param integer $loop_count If false the gif will not loop, if 0 it will loop endlessly, any other number it will loop that amount.
  1399. */
  1400. function setGifLoops($loop_count)
  1401. {
  1402. if($loop_count !== false)
  1403. {
  1404. $this->addCommand('-loop_output', $loop_count);
  1405. }
  1406. }
  1407. /**
  1408. * @access public
  1409. * @depreciated
  1410. * @see PHPVideoToolkit::setVideoDimensions()
  1411. */
  1412. function setVideoOutputDimensions($width, $height=null)
  1413. {
  1414. return $this->setVideoDimensions($width, $height);
  1415. }
  1416. /**
  1417. * Sets the video output dimensions (in pixels)
  1418. *
  1419. * @access public
  1420. * @param mixed $width If an integer height also has to be specified, otherwise you can use one of the class constants
  1421. * PHPVIDEOTOOLKIT_SIZE_SAS = Same as input source
  1422. * PHPVIDEOTOOLKIT_SIZE_SQCIF = 128 x 96
  1423. * PHPVIDEOTOOLKIT_SIZE_QCIF = 176 x 144
  1424. * PHPVIDEOTOOLKIT_SIZE_CIF = 352 x 288
  1425. * PHPVIDEOTOOLKIT_SIZE_4CIF = 704 x 576
  1426. * PHPVIDEOTOOLKIT_SIZE_QQVGA = 160 x 120
  1427. * PHPVIDEOTOOLKIT_SIZE_QVGA = 320 x 240
  1428. * PHPVIDEOTOOLKIT_SIZE_VGA = 640 x 480
  1429. * PHPVIDEOTOOLKIT_SIZE_SVGA = 800 x 600
  1430. * PHPVIDEOTOOLKIT_SIZE_XGA = 1024 x 768
  1431. * PHPVIDEOTOOLKIT_SIZE_UXGA = 1600 x 1200
  1432. * PHPVIDEOTOOLKIT_SIZE_QXGA = 2048 x 1536
  1433. * PHPVIDEOTOOLKIT_SIZE_SXGA = 1280 x 1024
  1434. * PHPVIDEOTOOLKIT_SIZE_QSXGA = 2560 x 2048
  1435. * PHPVIDEOTOOLKIT_SIZE_HSXGA = 5120 x 4096
  1436. * PHPVIDEOTOOLKIT_SIZE_WVGA = 852 x 480
  1437. * PHPVIDEOTOOLKIT_SIZE_WXGA = 1366 x 768
  1438. * PHPVIDEOTOOLKIT_SIZE_WSXGA = 1600 x 1024
  1439. * PHPVIDEOTOOLKIT_SIZE_WUXGA = 1920 x 1200
  1440. * PHPVIDEOTOOLKIT_SIZE_WOXGA = 2560 x 1600
  1441. * PHPVIDEOTOOLKIT_SIZE_WQSXGA = 3200 x 2048
  1442. * PHPVIDEOTOOLKIT_SIZE_WQUXGA = 3840 x 2400
  1443. * PHPVIDEOTOOLKIT_SIZE_WHSXGA = 6400 x 4096
  1444. * PHPVIDEOTOOLKIT_SIZE_WHUXGA = 7680 x 4800
  1445. * PHPVIDEOTOOLKIT_SIZE_CGA = 320 x 200
  1446. * PHPVIDEOTOOLKIT_SIZE_EGA = 640 x 350
  1447. * PHPVIDEOTOOLKIT_SIZE_HD480 = 852 x 480
  1448. * PHPVIDEOTOOLKIT_SIZE_HD720 = 1280 x 720
  1449. * PHPVIDEOTOOLKIT_SIZE_HD1080 = 1920 x 1080
  1450. * @param integer $height
  1451. * @return boolean
  1452. */
  1453. function setVideoDimensions($width, $height=null)
  1454. {
  1455. if($height === null || $height === true)
  1456. {
  1457. // validate input
  1458. if(!in_array($width, array(PHPVIDEOTOOLKIT_SIZE_SAS, PHPVIDEOTOOLKIT_SIZE_SQCIF, PHPVIDEOTOOLKIT_SIZE_QCIF, PHPVIDEOTOOLKIT_SIZE_CIF, PHPVIDEOTOOLKIT_SIZE_4CIF, PHPVIDEOTOOLKIT_SIZE_QQVGA, PHPVIDEOTOOLKIT_SIZE_QVGA, PHPVIDEOTOOLKIT_SIZE_VGA, PHPVIDEOTOOLKIT_SIZE_SVGA, PHPVIDEOTOOLKIT_SIZE_XGA, PHPVIDEOTOOLKIT_SIZE_UXGA, PHPVIDEOTOOLKIT_SIZE_QXGA, PHPVIDEOTOOLKIT_SIZE_SXGA, PHPVIDEOTOOLKIT_SIZE_QSXGA, PHPVIDEOTOOLKIT_SIZE_HSXGA, PHPVIDEOTOOLKIT_SIZE_WVGA, PHPVIDEOTOOLKIT_SIZE_WXGA, PHPVIDEOTOOLKIT_SIZE_WSXGA, PHPVIDEOTOOLKIT_SIZE_WUXGA, PHPVIDEOTOOLKIT_SIZE_WOXGA, PHPVIDEOTOOLKIT_SIZE_WQSXGA, PHPVIDEOTOOLKIT_SIZE_WQUXGA, PHPVIDEOTOOLKIT_SIZE_WHSXGA, PHPVIDEOTOOLKIT_SIZE_WHUXGA, PHPVIDEOTOOLKIT_SIZE_CGA, PHPVIDEOTOOLKIT_SIZE_EGA, PHPVIDEOTOOLKIT_SIZE_HD480, PHPVIDEOTOOLKIT_SIZE_HD720, PHPVIDEOTOOLKIT_SIZE_HD1080)))
  1459. {
  1460. return $this->_raiseError('setVideoOutputDimensions_valid_format', array('format'=>$format));
  1461. //<- exits
  1462. }
  1463. if($width === PHPVIDEOTOOLKIT_SIZE_SAS)
  1464. {
  1465. // and override is made so no command is added in the hope that ffmpeg will just output the source
  1466. if($height === true)
  1467. {
  1468. return true;
  1469. }
  1470. // get the file info
  1471. $info = $this->getFileInfo();
  1472. if(!isset($info['video']) || !isset($info['video']['dimensions']))
  1473. {
  1474. return $this->_raiseError('setVideoOutputDimensions_sas_dim');
  1475. }
  1476. else
  1477. {
  1478. $width = $info['video']['dimensions']['width'].'x'.$info['video']['dimensions']['height'];
  1479. }
  1480. }
  1481. }
  1482. else
  1483. {
  1484. // check that the width and height are even
  1485. if($width % 2 !== 0 || $height % 2 !== 0)
  1486. {
  1487. return $this->_raiseError('setVideoOutputDimensions_valid_integer');
  1488. //<- exits
  1489. }
  1490. $width = $width.'x'.$height;
  1491. }
  1492. $this->addCommand('-s', $width);
  1493. return true;
  1494. }
  1495. /**
  1496. * Sets the video aspect ratio
  1497. *
  1498. * @access public
  1499. * @param string|integer $ratio Valid values are PHPVIDEOTOOLKIT_RATIO_STANDARD, PHPVIDEOTOOLKIT_RATIO_WIDE, PHPVIDEOTOOLKIT_RATIO_CINEMATIC, or '4:3', '16:9', '1.85'
  1500. * @return boolean
  1501. */
  1502. function setVideoAspectRatio($ratio)
  1503. {
  1504. if(!in_array($ratio, array(PHPVIDEOTOOLKIT_RATIO_STANDARD, PHPVIDEOTOOLKIT_RATIO_WIDE, PHPVIDEOTOOLKIT_RATIO_CINEMATIC)))
  1505. {
  1506. return $this->_raiseError('setVideoAspectRatio_valid_ratio', array('ratio'=>$ratio));
  1507. }
  1508. $this->addCommand('-aspect', $ratio);
  1509. return true;
  1510. }
  1511. /**
  1512. * Sets the frame rate of the video
  1513. *
  1514. * @access public
  1515. * @param string|integer $fps 1 being 1 frame per second, 1:2 being 0.5 frames per second
  1516. * @return boolean
  1517. */
  1518. function setVideoFrameRate($fps)
  1519. {
  1520. return $this->addCommand('-r', $fps);
  1521. }
  1522. /**
  1523. * Extracts a segment of video and/or audio
  1524. * (Note; If set to 1 and the duration set by $extract_begin_timecode and $extract_end_timecode is equal to 1 you get more than one frame.
  1525. * For example if you set $extract_begin_timecode='00:00:00' and $extract_end_timecode='00:00:01' you might expect because the time span is
  1526. * 1 second only to get one frame if you set $frames_per_second=1. However this is not correct. The timecode you set in $extract_begin_timecode
  1527. * acts as the beginning frame. Thus in this example the first frame exported will be from the very beginning of the video, the video will
  1528. * then move onto the next frame and export a frame there. Therefore if you wish to export just one frame from one position in the video,
  1529. * say 1 second in you should set $extract_begin_timecode='00:00:01' and set $extract_end_timecode='00:00:01'.)
  1530. *
  1531. * @access public
  1532. * @param string $extract_begin_timecode A timecode (hh:mm:ss.fn - you can change the timecode format by changing the $timecode_format param
  1533. * it obeys the formatting of PHPVideoToolkit::formatTimecode(), see below for more info)
  1534. * @param string|integer|boolean $extract_end_timecode A timecode (hh:mm:ss.fn - you can change the timecode format by changing the $timecode_format param
  1535. * it obeys the formatting of PHPVideoToolkit::formatTimecode(), see below for more info)
  1536. * @param integer $timecode_format The format of the $extract_begin_timecode and $extract_end_timecode timecodes are being given in.
  1537. * default '%hh:%mm:%ss'
  1538. * - %hh (hours) representative of hours
  1539. * - %mm (minutes) representative of minutes
  1540. * - %ss (seconds) representative of seconds
  1541. * - %fn (frame number) representative of frames (of the current second, not total frames)
  1542. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  1543. * - %ft (frames total) representative of total frames (ie frame number)
  1544. * - %st (seconds total) representative of total seconds (rounded).
  1545. * - %sf (seconds floored) representative of total seconds (floored).
  1546. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  1547. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  1548. * @param boolean $check_frames_exist Determines if a frame exists check should be made to ensure the timecode given by $extract_end_timecode
  1549. * actually exists.
  1550. */
  1551. public function extractSegment($extract_begin_timecode, $extract_end_timecode, $timecode_format='%hh:%mm:%ss.%fn', $frames_per_second=false, $check_frames_exist=true)
  1552. {
  1553. // check for frames per second, if it's not set auto set it.
  1554. if($frames_per_second === false)
  1555. {
  1556. $info = $this->getFileInfo();
  1557. $frames_per_second = $info['duration']['timecode']['frames']['frame_rate'];
  1558. }
  1559. // check if frame exists
  1560. if($check_frames_exist)
  1561. {
  1562. if($info['duration']['seconds'] < floatval($this->formatTimecode($extract_end_timecode, $timecode_format, '%ss.%ms', $frames_per_second)))
  1563. {
  1564. // the input has not returned any video data so the frame rate can not be guessed
  1565. return $this->_raiseError('extractSegment_end_timecode');
  1566. }
  1567. else if($extract_end_timecode !== false && $info['duration']['seconds'] < floatval($this->formatTimecode($extract_begin_timecode, $timecode_format, '%ss.%ms', $frames_per_second)))
  1568. {
  1569. // the input has not returned any video data so the frame rate can not be guessed
  1570. return $this->_raiseError('extractSegment_begin_timecode');
  1571. }
  1572. }
  1573. // format the begin timecode if the timecode format is not already ok.
  1574. $begin_position = (float) $this->formatTimecode($extract_begin_timecode, $timecode_format, '%ss.%ms', $frames_per_second);
  1575. if($timecode_format !== '%hh:%mm:%ss.%ms')
  1576. {
  1577. $extract_begin_timecode = $this->formatTimecode($extract_begin_timecode, $timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1578. }
  1579. $this->addCommand('-ss', $extract_begin_timecode);
  1580. // allows for exporting the entire timeline
  1581. if($extract_end_timecode !== false)
  1582. {
  1583. $end_position = (float) $this->formatTimecode($extract_end_timecode, $timecode_format, '%ss.%ms', $frames_per_second);
  1584. // format the end timecode if the timecode format is not already ok.
  1585. if($timecode_format !== '%hh:%mm:%ss.%ms')
  1586. {
  1587. $extract_end_timecode = $this->formatTimecode($extract_end_timecode, $timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1588. }
  1589. $this->addCommand('-t', $end_position-$begin_position);
  1590. }
  1591. return true;
  1592. }
  1593. /**
  1594. * Extracts frames from a video.
  1595. * (Note; If set to 1 and the duration set by $extract_begin_timecode and $extract_end_timecode is equal to 1 you get more than one frame.
  1596. * For example if you set $extract_begin_timecode='00:00:00' and $extract_end_timecode='00:00:01' you might expect because the time span is
  1597. * 1 second only to get one frame if you set $frames_per_second=1. However this is not correct. The timecode you set in $extract_begin_timecode
  1598. * acts as the beginning frame. Thus in this example the first frame exported will be from the very beginning of the video, the video will
  1599. * then move onto the next frame and export a frame there. Therefore if you wish to export just one frame from one position in the video,
  1600. * say 1 second in you should set $extract_begin_timecode='00:00:01' and set $extract_end_timecode='00:00:01'.)
  1601. *
  1602. * @access public
  1603. * @param string $extract_begin_timecode A timecode (hh:mm:ss.fn - you can change the timecode format by changing the $timecode_format param
  1604. * it obeys the formatting of PHPVideoToolkit::formatTimecode(), see below for more info)
  1605. * @param string|integer|boolean $extract_end_timecode A timecode (hh:mm:ss.fn - you can change the timecode format by changing the $timecode_format param
  1606. * it obeys the formatting of PHPVideoToolkit::formatTimecode(), see below for more info), or false
  1607. * if all frames from the begin timecode are to be exported. (Boolean added by Matthias. Thanks. 12th March 2007)
  1608. * @param boolean|integer $frames_per_second The number of frames per second to extract. If left as default false, then the number of frames per second
  1609. * will be automagically gained from PHPVideoToolkit::fileGetInfo();
  1610. * @param boolean|integer $frame_limit Frame limiter. If set to false then all the frames will be exported from the given time codes, however
  1611. * if you wish to set a export limit to the number of frames that are exported you can set an integer. For example; if you set
  1612. * $extract_begin_timecode='00:00:11.01', $extract_end_timecode='00:01:10.01', $frames_per_second=1, you will get one frame for every second
  1613. * in the video between 00:00:11 and 00:01:10 (ie 60 frames), however if you ant to artificially limit this to exporting only ten frames
  1614. * then you set $frame_limit=10. You could of course alter the timecode to reflect you desired frame number, however there are situations
  1615. * when a shortcut such as this is useful and necessary.
  1616. * @param integer $timecode_format The format of the $extract_begin_timecode and $extract_end_timecode timecodes are being given in.
  1617. * default '%hh:%mm:%ss'
  1618. * - %hh (hours) representative of hours
  1619. * - %mm (minutes) representative of minutes
  1620. * - %ss (seconds) representative of seconds
  1621. * - %fn (frame number) representative of frames (of the current second, not total frames)
  1622. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  1623. * - %ft (frames total) representative of total frames (ie frame number)
  1624. * - %st (seconds total) representative of total seconds (rounded).
  1625. * - %sf (seconds floored) representative of total seconds (floored).
  1626. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  1627. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  1628. */
  1629. function extractFrames($extract_begin_timecode, $extract_end_timecode, $frames_per_second=false, $frame_limit=false, $timecode_format='%hh:%mm:%ss.%fn', $check_frames_exist=true)
  1630. {
  1631. // are we autoguessing the frame rate?
  1632. if($frames_per_second === false || $check_frames_exist)
  1633. {
  1634. // get the file info, will exit if no input has been set
  1635. $info = $this->getFileInfo();
  1636. if($info === false || !isset($info['video']))
  1637. {
  1638. // the input has not returned any video data so the frame rate can not be guessed
  1639. return $this->_raiseError('extractFrame_video_frame_rate_404');
  1640. }
  1641. }
  1642. // check to see if we have to get the fps of the input movie
  1643. if($frames_per_second === false)
  1644. {
  1645. $frames_per_second = $info['video']['frame_rate'];
  1646. }
  1647. // check if frame exists
  1648. if($check_frames_exist)
  1649. {
  1650. if($info['video']['frame_count'] < $this->formatTimecode($extract_end_timecode, $timecode_format, '%ft', $frames_per_second))
  1651. {
  1652. // the input has not returned any video data so the frame rate can not be guessed
  1653. return $this->_raiseError('extractFrames_video_end_frame_count');
  1654. }
  1655. else if($extract_end_timecode !== false && $info['video']['frame_count'] < $this->formatTimecode($extract_begin_timecode, $timecode_format, '%ft', $frames_per_second))
  1656. {
  1657. // the input has not returned any video data so the frame rate can not be guessed
  1658. return $this->_raiseError('extractFrames_video_begin_frame_count');
  1659. }
  1660. }
  1661. // disable audio output
  1662. $this->disableAudio();
  1663. // format the begin timecode if the timecode format is not already ok.
  1664. if($timecode_format !== '%hh:%mm:%ss.%ms')
  1665. {
  1666. $extract_begin_timecode = $this->formatTimecode($extract_begin_timecode, $timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1667. }
  1668. $this->addCommand('-ss', $extract_begin_timecode);
  1669. // added by Matthias on 12th March 2007
  1670. // allows for exporting the entire timeline
  1671. if($extract_end_timecode !== false)
  1672. {
  1673. // format the end timecode if the timecode format is not already ok.
  1674. if($timecode_format !== '%hh:%mm:%ss.%ms')
  1675. {
  1676. $extract_end_timecode = $this->formatTimecode($extract_end_timecode, $timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1677. }
  1678. $this->addCommand('-t', $extract_end_timecode);
  1679. }
  1680. // set the output frame rate
  1681. $this->setVideoFrameRate($frames_per_second);
  1682. // do we need to limit the number of frames outputted?
  1683. if($frame_limit !== false)
  1684. {
  1685. $this->addCommand('-vframes', $frame_limit);
  1686. }
  1687. $this->_image_output_timecode_start = $extract_begin_timecode;
  1688. $this->_image_output_timecode_fps = $frames_per_second;
  1689. }
  1690. /**
  1691. * Extracts exactly one frame
  1692. *
  1693. * @access public
  1694. * @uses $toolkit->extractFrames
  1695. * @param string $frame_timecode A timecode (hh:mm:ss.fn) where fn is the frame number of that second
  1696. * @param integer|boolean $frames_per_second The frame rate of the movie. If left as the default, false. We will use PHPVideoToolkit::getFileInfo() to get
  1697. * the actual frame rate. It is recommended that it is left as false because an incorrect frame rate may produce unexpected results.
  1698. * @param integer $timecode_format The format of the $extract_begin_timecode and $extract_end_timecode timecodes are being given in.
  1699. * default '%hh:%mm:%ss'
  1700. * - %hh (hours) representative of hours
  1701. * - %mm (minutes) representative of minutes
  1702. * - %ss (seconds) representative of seconds
  1703. * - %fn (frame number) representative of frames (of the current second, not total frames)
  1704. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  1705. * - %ft (frames total) representative of total frames (ie frame number)
  1706. * - %st (seconds total) representative of total seconds (rounded).
  1707. * - %sf (seconds floored) representative of total seconds (floored).
  1708. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  1709. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  1710. * @param boolean $check_frame_exists Makes an explicit check to see if the frame exists, default = true.
  1711. * Thanks to Istvan Szakacs for suggesting this check. Note, to improve performance disable this check.
  1712. */
  1713. function extractFrame($frame_timecode, $frames_per_second=false, $frame_timecode_format='%hh:%mm:%ss.%fn', $check_frame_exists=true)
  1714. {
  1715. // get the file info, will exit if no input has been set
  1716. if($check_frame_exists || $frames_per_second === false)
  1717. {
  1718. $info = $this->getFileInfo();
  1719. if($info === false || !isset($info['video']))
  1720. {
  1721. // the input has not returned any video data so the frame rate can not be guessed
  1722. return $this->_raiseError('extractFrame_video_info_404');
  1723. }
  1724. }
  1725. // are we autoguessing the frame rate?
  1726. if($frames_per_second === false)
  1727. {
  1728. if(!isset($info['video']['frame_rate']))
  1729. {
  1730. // the input has not returned any video data so the frame rate can not be guessed
  1731. return $this->_raiseError('extractFrame_video_frame_rate_404');
  1732. }
  1733. $frames_per_second = $info['video']['frame_rate'];
  1734. }
  1735. // check if frame exists
  1736. if($check_frame_exists)
  1737. {
  1738. if($info['video']['frame_count'] < $this->formatTimecode($frame_timecode, $frame_timecode_format, '%ft', $frames_per_second))
  1739. {
  1740. // the input has not returned any video data so the frame rate can not be guessed
  1741. return $this->_raiseError('extractFrame_video_frame_count');
  1742. }
  1743. }
  1744. // format the frame details if the timecode format is not already ok.
  1745. if($frame_timecode_format !== '%hh:%mm:%ss.%ms')
  1746. {
  1747. $frame_timecode = $this->formatTimecode($frame_timecode, $frame_timecode_format, '%hh:%mm:%ss.%ms', $frames_per_second);
  1748. }
  1749. $this->_single_frame_extraction = 1;
  1750. // we will limit the number of frames produced so the desired frame is the last image
  1751. // this way we limit the cpu usage of ffmpeg
  1752. // Thanks to Istvan Szakacs for pointing out that ffmpeg can export frames using the -ss hh:mm:ss[.xxx]
  1753. // it has saved a lot of cpu intensive processes.
  1754. $this->extractFrames($frame_timecode, $frame_timecode, $frames_per_second, 1, '%hh:%mm:%ss.%ms', false);
  1755. // register the post tidy process
  1756. // $this->registerPostProcess('_extractFrameTidy', $this);
  1757. }
  1758. // /**
  1759. // * Tidies up after ffmpeg exports all frames from one second of video.
  1760. // *
  1761. // * @access public
  1762. // * @uses $toolkit->extractFrames
  1763. // * @param string $frame_timecode A timecode (hh:mm:ss.fn) where fn is the frame number of that second
  1764. // * @param integer|boolean $frames_per_second The frame rate of the movie. If left as the default, false. We will use PHPVideoToolkit::getFileInfo() to get
  1765. // * the actual frame rate. It is recommended that it is left as false because an incorrect frame rate may produce unexpected results.
  1766. // */
  1767. // function _extractFrameTidy(&$files)
  1768. // {
  1769. // $frame_number = 1;
  1770. // $frame_file = array();
  1771. // // print_r($files);
  1772. // foreach($files as $file=>$filename)
  1773. // {
  1774. // // print_R(array($this->_single_frame_extraction, $frame_number));
  1775. // if($this->_single_frame_extraction == $frame_number)
  1776. // {
  1777. // // leave this file alone as it is the required frame
  1778. // $frame_file[$file] = $filename;
  1779. // }
  1780. // else
  1781. // {
  1782. // // add the frame to the unlink files list
  1783. // array_push($this->_unlink_files, $file);
  1784. // }
  1785. // $frame_number += 1;
  1786. // }
  1787. // // print_r($frame_file);
  1788. // // update the files list
  1789. // $files = $frame_file;
  1790. // return true;
  1791. // }
  1792. /**
  1793. * Adds a watermark to the outputted files. This effects both video and image output.
  1794. *
  1795. * @access public
  1796. * @param string $watermark_url The absolute path to the watermark image.
  1797. * @param string $vhook The absolute path to the ffmpeg vhook watermark library.
  1798. * @param string $watermark_options Any additional options to supply to the vhook.
  1799. */
  1800. function addWatermark($watermark_url, $vhook=PHPVIDEOTOOLKIT_FFMPEG_WATERMARK_VHOOK, $watermark_options=false)
  1801. {
  1802. // check to see if the ffmpeg binary has support for vhooking
  1803. if(!$this->hasVHookSupport())
  1804. {
  1805. return $this->_raiseError('addWatermark_vhook_disabled');
  1806. }
  1807. // does the file exist?
  1808. if(!is_file($watermark_url))
  1809. {
  1810. return $this->_raiseError('addWatermark_img_404', array('watermark'=>$watermark_url));
  1811. }
  1812. $this->addCommand('-vhook', $vhook.' -f '.$watermark_url.($watermark_options !== false ? ' '.$watermark_options : ''));
  1813. }
  1814. /**
  1815. * Adds a watermark to the outputted image files using the PHP GD module.
  1816. * This effects only image output.
  1817. *
  1818. * @access public
  1819. * @param string $watermark_url The absolute path to the watermark image.
  1820. */
  1821. function addGDWatermark($watermark_url, $options=array('x-offset'=>0, 'y-offset'=>0, 'position'=>'bottom-right'))
  1822. {
  1823. // does the file exist?
  1824. if(!is_file($watermark_url))
  1825. {
  1826. return $this->_raiseError('addWatermark_img_404', array('watermark'=>$watermark_url));
  1827. }
  1828. // save the watermark_url
  1829. $this->_watermark_url = $watermark_url;
  1830. $this->_watermark_options = array_merge(array('x-offset'=>0, 'y-offset'=>0, 'position'=>'bottom-right'), $options);
  1831. // register the post process
  1832. $this->registerPostProcess('_addGDWatermark', $this);
  1833. }
  1834. /**
  1835. * Adds watermark to any outputted images via GD instead of using vhooking.
  1836. *
  1837. * @access private
  1838. * @param array $files An array of image files.
  1839. * @return array
  1840. */
  1841. function _addGDWatermark($files)
  1842. {
  1843. // create the watermark resource and give it alpha blending
  1844. $info = pathinfo($this->_watermark_url);
  1845. switch(strtolower($info['extension']))
  1846. {
  1847. case 'jpeg' :
  1848. case 'jpg' :
  1849. $watermark = imagecreatefromjpeg($this->_watermark_url);
  1850. break;
  1851. case 'gif' :
  1852. $watermark = imagecreatefromgif($this->_watermark_url);
  1853. break;
  1854. case 'png' :
  1855. $watermark = imagecreatefrompng($this->_watermark_url);
  1856. break;
  1857. default :
  1858. return false;
  1859. }
  1860. imagealphablending($watermark, true);
  1861. imagesavealpha($watermark, true);
  1862. // get the watermark dimensions
  1863. $watermark_width = imagesx($watermark);
  1864. $watermark_height = imagesy($watermark);
  1865. // $image = imagecreatetruecolor($watermark_width, $watermark_height);
  1866. // loop and watermark each file
  1867. $blended_files = array();
  1868. foreach($files as $file=>$filename)
  1869. {
  1870. // detect the file extension and create the resource from them appropriate function
  1871. $info = pathinfo($file);
  1872. $quality = $output_function = null;
  1873. switch(strtolower($info['extension']))
  1874. {
  1875. case 'jpeg' :
  1876. case 'jpg' :
  1877. $quality = 80;
  1878. $output_function = 'imagejpeg';
  1879. $image = imagecreatefromjpeg($file);
  1880. break;
  1881. case 'gif' :
  1882. $output_function = 'imagegif';
  1883. $image = imagecreatefromgif($file);
  1884. break;
  1885. case 'png' :
  1886. $quality = 9;
  1887. $output_function = 'imagepng';
  1888. $image = imagecreatefrompng($file);
  1889. break;
  1890. default :
  1891. continue 1;
  1892. }
  1893. // the dimensions will/should be the same for each image however still best to check
  1894. $image_width = imagesx($image);
  1895. $image_height = imagesy($image);
  1896. // calculate where to position the watermark
  1897. $dest_x = 0;
  1898. $dest_y = 0;
  1899. switch($this->_watermark_options['position'])
  1900. {
  1901. case 'top-left' :
  1902. $dest_x = 0;
  1903. $dest_y = 0;
  1904. break;
  1905. case 'top-middle' :
  1906. $dest_x = ($image_width-$watermark_width)/2;
  1907. $dest_y = 0;
  1908. break;
  1909. case 'top-right' :
  1910. $dest_x = $image_width-$watermark_width;
  1911. $dest_y = 0;
  1912. break;
  1913. case 'center-left' :
  1914. $dest_x = 0;
  1915. $dest_y = ($image_height-$watermark_height)/2;
  1916. break;
  1917. case 'center-middle' :
  1918. $dest_x = ($image_width-$watermark_width)/2;
  1919. $dest_y = ($image_height-$watermark_height)/2;
  1920. break;
  1921. case 'center-right' :
  1922. $dest_x = $image_width-$watermark_width;
  1923. $dest_y = ($image_height-$watermark_height)/2;
  1924. break;
  1925. case 'bottom-left' :
  1926. $dest_x = 0;
  1927. $dest_y = $image_height - $watermark_height;
  1928. break;
  1929. case 'bottom-middle' :
  1930. $dest_x = ($image_width-$watermark_width)/2;
  1931. $dest_y = $image_height - $watermark_height;
  1932. break;
  1933. case 'bottom-right' :
  1934. default :
  1935. $dest_x = $image_width-$watermark_width;
  1936. $dest_y = $image_height - $watermark_height;
  1937. break;
  1938. }
  1939. $dest_x += $this->_watermark_options['x-offset'];
  1940. $dest_y += $this->_watermark_options['y-offset'];
  1941. // copy the watermark to the new image
  1942. // print_r(array($this->_watermark_url, $image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height));
  1943. imagecopy($image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
  1944. // delete the old image
  1945. unlink($file);
  1946. // save the new image in place of the old
  1947. $output_function($image, $file, $quality);
  1948. // remove the image resouce
  1949. imagedestroy($image);
  1950. array_push($blended_files, $file);
  1951. }
  1952. // remove the watermark resource
  1953. imagedestroy($watermark);
  1954. return $blended_files;
  1955. }
  1956. // /**
  1957. // * This will overlay an audio file over the top of a video file
  1958. // **/
  1959. // function overlayAudio($audio_file)
  1960. // {
  1961. // $this->addCommand('-newaudio', '');
  1962. // }
  1963. /**
  1964. * This will adjust the audio volume.
  1965. *
  1966. * @access public
  1967. * @param integer $vol 256 = normal
  1968. **/
  1969. function adjustVolume($vol=256)
  1970. {
  1971. $this->addCommand('-vol', '');
  1972. }
  1973. /**
  1974. * This process will combine the original input video with the video specified by this function.
  1975. * This function accepts more than one video as arguments. They will be added in order of the arguments.
  1976. * ie. input_video -> video1 -> video2 etc
  1977. * The process of doing this can take a long time as each incoming video has to be first converted
  1978. * into a format that accepts joining. The default joining codec is "mpg". However for almost lossless
  1979. * quality you can use the "yuv4mpegpipe" format. This is of course dependent upon your ffmpeg binary.
  1980. * You can check to see if you server supports yuv4mpegpipe by typing "ffmpeg -formats" into the
  1981. * command line. If you want to use the yuv4mpegpipe format you can add the flag, FFMPEG_USE_HQ_JOIN to the
  1982. * end of the video inputs. WARNING: High Quality joins will take longer to process. (well duh!)
  1983. *
  1984. * @access public
  1985. * @param $video1, $video2, $video3... $video(n) Paths of videos to attach to the input video.
  1986. * @param $flag integer FFMPEG_USE_HQ_JOIN If you wish to use the yuv4mpegpipe format for join add this to the end of the video list.
  1987. */
  1988. function addVideo()
  1989. {
  1990. $videos = func_get_args();
  1991. $videos_length = count($videos);
  1992. // is last arg the hq join flag
  1993. // check to see if a starter file has been added, if not set the input as an array
  1994. if($this->_input_file === null)
  1995. {
  1996. $this->_input_file = array();
  1997. }
  1998. // if the input file is already set as a string that means as start file has been added so absorb into the input array
  1999. else if(is_string($this->_input_file))
  2000. {
  2001. $this->_input_file = array($this->_input_file);
  2002. }
  2003. foreach($videos as $key=>$file)
  2004. {
  2005. if(!preg_match('/\%([0-9]+)d/', $file) && strpos($file, '%d') === false && !is_file($file))
  2006. {
  2007. // input file not valid
  2008. return $this->_raiseError('addVideo_file_404', array('file'=>$file));
  2009. //<- exits
  2010. }
  2011. array_push($this->_input_file, $file);
  2012. // array_push($this->_input_file, escapeshellarg($file));
  2013. }
  2014. }
  2015. /**
  2016. * @access public
  2017. * @uses addVideo()
  2018. */
  2019. function addVideos()
  2020. {
  2021. $videos = func_get_args();
  2022. call_user_func_array(array(&$this, 'addVideo'), $videos);
  2023. }
  2024. /**
  2025. * Sets the output.
  2026. *
  2027. * @access public
  2028. * @param string $output_directory The directory to output the command output to
  2029. * @param string $output_name The filename to output to.
  2030. * (Note; if you are outputting frames from a video then you will need to add an extra item to the output_name. The output name you set is required
  2031. * to contain '%d'. '%d' is replaced by the image number. Thus entering setting output_name $output_name='img%d.jpg' will output
  2032. * 'img1.jpg', 'img2.jpg', etc... However 'img%03d.jpg' generates `img001.jpg', `img002.jpg', etc...)
  2033. * @param boolean $overwrite_mode Accepts one of the following class constants
  2034. * - PHPVIDEOTOOLKIT_OVERWRITE_FAIL - This produces an error if there is a file conflict and the processing is halted.
  2035. * - PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE - This continues with the processing but no file overwrite takes place. The processed file is left in the temp directory
  2036. * for you to manually move.
  2037. * - PHPVIDEOTOOLKIT_OVERWRITE_EXISTING - This will replace any existing files with the freshly processed ones.
  2038. * - PHPVIDEOTOOLKIT_OVERWRITE_UNIQUE - This will appended every output with a unique hash so that the filesystem is preserved.
  2039. * @return boolean false on error encountered, true otherwise
  2040. */
  2041. function setOutput($output_directory, $output_name, $overwrite_mode=PHPVIDEOTOOLKIT_OVERWRITE_FAIL)
  2042. {
  2043. // check if directoy exists
  2044. if(!is_dir($output_directory))
  2045. {
  2046. return $this->_raiseError('setOutput_output_dir_404', array('dir'=>$output_directory));
  2047. //<- exits
  2048. }
  2049. // check if directory is writeable
  2050. if(!is_writable($output_directory))
  2051. {
  2052. return $this->_raiseError('setOutput_output_dir_writable', array('dir'=>$output_directory));
  2053. //<- exits
  2054. }
  2055. $process_name = '';
  2056. // check to see if a output delimiter is set
  2057. $has_d = preg_match('/\%([0-9]+)d/', $output_name) || strpos($output_name, '%d') !== false;
  2058. if($has_d)
  2059. {
  2060. return $this->_raiseError('setOutput_%d_depreciated');
  2061. //<- exits
  2062. }
  2063. else
  2064. {
  2065. // determine if the extension is an image. If it is then we will be extracting frames so check for %d
  2066. $output_name_info = pathinfo($output_name);
  2067. $is_image = in_array(strtolower($output_name_info['extension']), array('jpg', 'jpeg', 'png'));
  2068. $is_gif = strtolower($output_name_info['extension']) === 'gif';
  2069. // NOTE: for now we'll just stick to the common image formats, SUBNOTE: gif is ignore because ffmpeg can create animated gifs
  2070. if($this->_single_frame_extraction !== null && strpos($output_name, '%timecode') !== false && !(preg_match('/\%index/', $output_name) || strpos($output_name, '%index') !== false) && $is_image)
  2071. {
  2072. return $this->_raiseError('setOutput_%_missing');
  2073. //<- exits
  2074. }
  2075. $process_name = '.'.$output_name_info['extension'];
  2076. // print_r(array($is_image, ($this->_single_frame_extraction !== null && $is_gif)));
  2077. if($is_image || ($this->_single_frame_extraction !== null && $is_gif))
  2078. {
  2079. $process_name = '-%12d'.$process_name;
  2080. }
  2081. }
  2082. // set the output address
  2083. $this->_output_address = $output_directory.$output_name;
  2084. // set the processing address in the temp folder so it does not conflict with any other conversions
  2085. $this->_process_address = $this->_tmp_directory.$this->unique().$process_name;
  2086. $this->_overwrite_mode = $overwrite_mode;
  2087. return true;
  2088. }
  2089. /**
  2090. * Sets a constant quality value to the encoding. (but a variable bitrate)
  2091. *
  2092. * @param integer $quality The quality to adhere to. 100 is highest quality, 1 is the lowest quality
  2093. */
  2094. function setConstantQuality($quality)
  2095. {
  2096. // interpret quality into ffmpeg value
  2097. $quality = 31 - round(($quality/100) * 31);
  2098. if($quality > 31)
  2099. {
  2100. $quality = 31;
  2101. }
  2102. else if($quality < 1)
  2103. {
  2104. $quality = 1;
  2105. }
  2106. $this->addCommand('-qscale', $quality);
  2107. }
  2108. /**
  2109. * Translates a number of seconds to a timecode.
  2110. * NOTE: this is now a depreciated, use formatSeconds() instead.
  2111. *
  2112. * @depreciated Use formatSeconds() instead.
  2113. * @access public
  2114. * @uses PHPVideoToolkit::formatSeconds()
  2115. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  2116. */
  2117. function secondsToTimecode($input_seconds=0)
  2118. {
  2119. return $this->formatSeconds($input_seconds, '%hh:%mm:%ss');
  2120. }
  2121. /**
  2122. * Translates a timecode to the number of seconds.
  2123. * NOTE: this is now a depreciated, use formatTimecode() instead.
  2124. *
  2125. * @depreciated Use formatTimecode() instead.
  2126. * @access public
  2127. * @uses PHPVideoToolkit::formatTimecode()
  2128. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  2129. */
  2130. function timecodeToSeconds($input_timecode='00:00:00')
  2131. {
  2132. return $this->formatTimecode($input_timecode, '%hh:%mm:%ss', '%st');
  2133. }
  2134. /**
  2135. * Translates a number of seconds to a timecode.
  2136. *
  2137. * @access public
  2138. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  2139. * @param integer $return_format The format of the timecode to return. The default is
  2140. * default '%hh:%mm:%ss'
  2141. * - %hh (hours) representative of hours
  2142. * - %mm (minutes) representative of minutes
  2143. * - %ss (seconds) representative of seconds
  2144. * - %fn (frame number) representative of frames (of the current second, not total frames)
  2145. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  2146. * - %ft (frames total) representative of total frames (ie frame number)
  2147. * - %st (seconds total) representative of total seconds (rounded).
  2148. * - %sf (seconds floored) representative of total seconds (floored).
  2149. * - %sc (seconds ceiled) representative of total seconds (ceiled).
  2150. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  2151. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  2152. * @param mixed|boolean|integer $frames_per_second The number of frames per second to translate for. If left false
  2153. * the class automagically gets the fps from PHPVideoToolkit::getFileInfo(), but the input has to be set
  2154. * first for this to work properly.
  2155. * @param boolean $use_smart_values Default value is true, if a format is found (ie %ss - secs) but no higher format (ie %mm - mins)
  2156. * is found then if $use_smart_values is true the value of of the format will be totaled.
  2157. * @return string|integer Returns the timecode, but if $frames_per_second is not set and a frame rate lookup is required
  2158. * but can't be reached then -1 will be returned.
  2159. */
  2160. function formatSeconds($input_seconds, $return_format='%hh:%mm:%ss', $frames_per_second=false, $use_smart_values=true)
  2161. {
  2162. $timestamp = mktime(0, 0, $input_seconds, 0, 0);
  2163. $floored = floor($input_seconds);
  2164. $hours = $input_seconds > 3600 ? floor($input_seconds/3600) : 0;
  2165. $mins = date('i', $timestamp);
  2166. $searches = array();
  2167. $replacements = array();
  2168. // these ones are the simple replacements
  2169. // replace the hours
  2170. $using_hours = strpos($return_format, '%hh') !== false;
  2171. if($using_hours)
  2172. {
  2173. array_push($searches, '%hh');
  2174. array_push($replacements, $hours);
  2175. }
  2176. // replace the minutes
  2177. $using_mins = strpos($return_format, '%mm') !== false;
  2178. if($using_mins)
  2179. {
  2180. array_push($searches, '%mm');
  2181. // check if hours are being used, if not and hours are required enable smart minutes
  2182. if($use_smart_values === true && !$using_hours && $hours > 0)
  2183. {
  2184. $value = ($hours * 60) + $mins;
  2185. }
  2186. else
  2187. {
  2188. $value = $mins;
  2189. }
  2190. array_push($replacements, $value);
  2191. }
  2192. // replace the seconds
  2193. if(strpos($return_format, '%ss') !== false)
  2194. {
  2195. // check if hours are being used, if not and hours are required enable smart minutes
  2196. if($use_smart_values === true && !$using_mins && !$using_hours && $hours > 0)
  2197. {
  2198. $mins = ($hours * 60) + $mins;
  2199. }
  2200. // check if mins are being used, if not and hours are required enable smart minutes
  2201. if($use_smart_values === true && !$using_mins && $mins > 0)
  2202. {
  2203. $value = ($mins * 60) + date('s', $timestamp);
  2204. }
  2205. else
  2206. {
  2207. $value = date('s', $timestamp);
  2208. }
  2209. array_push($searches, '%ss');
  2210. array_push($replacements, $value);
  2211. }
  2212. // replace the milliseconds
  2213. if(strpos($return_format, '%ms') !== false)
  2214. {
  2215. $milli = round($input_seconds - $floored, 3);
  2216. $milli = substr($milli, 2);
  2217. $milli = empty($milli) ? '0' : $milli;
  2218. array_push($searches, '%ms');
  2219. array_push($replacements, $milli);
  2220. }
  2221. // replace the total seconds (rounded)
  2222. if(strpos($return_format, '%st') !== false)
  2223. {
  2224. array_push($searches, '%st');
  2225. array_push($replacements, round($input_seconds));
  2226. }
  2227. // replace the total seconds (floored)
  2228. if(strpos($return_format, '%sf') !== false)
  2229. {
  2230. array_push($searches, '%sf');
  2231. array_push($replacements, floor($input_seconds));
  2232. }
  2233. // replace the total seconds (ceiled)
  2234. if(strpos($return_format, '%sc') !== false)
  2235. {
  2236. array_push($searches, '%sc');
  2237. array_push($replacements, ceil($input_seconds));
  2238. }
  2239. // replace the total seconds
  2240. if(strpos($return_format, '%mt') !== false)
  2241. {
  2242. array_push($searches, '%mt');
  2243. array_push($replacements, round($input_seconds, 3));
  2244. }
  2245. // these are the more complicated as they depend on $frames_per_second / frames per second of the current input
  2246. $has_frames = strpos($return_format, '%fn') !== false;
  2247. $has_total_frames = strpos($return_format, '%ft') !== false;
  2248. if($has_frames || $has_total_frames)
  2249. {
  2250. // if the fps is false then we must automagically detect it from the input file
  2251. if($frames_per_second === false)
  2252. {
  2253. $info = $this->getFileInfo();
  2254. // check the information has been received
  2255. if($info === false || (!isset($info['video']) || !isset($info['video']['frame_rate'])))
  2256. {
  2257. // fps cannot be reached so return -1
  2258. return -1;
  2259. }
  2260. $frames_per_second = $info['video']['frame_rate'];
  2261. }
  2262. // replace the frames
  2263. $excess_frames = false;
  2264. if($has_frames)
  2265. {
  2266. $excess_frames = ceil(($input_seconds - $floored) * $frames_per_second);
  2267. // print_r(array($input_seconds, $excess_frames));
  2268. array_push($searches, '%fn');
  2269. array_push($replacements, $excess_frames);
  2270. }
  2271. // replace the total frames (ie frame number)
  2272. if($has_total_frames)
  2273. {
  2274. $round_frames = $floored * $frames_per_second;
  2275. if(!$excess_frames)
  2276. {
  2277. $excess_frames = ceil(($input_seconds - $floored) * $frames_per_second);
  2278. }
  2279. array_push($searches, '%ft');
  2280. array_push($replacements, $round_frames + $excess_frames);
  2281. }
  2282. }
  2283. // print_r(array($searches, $replacements, $return_format));
  2284. // print_r(array($input_seconds, $timestamp, $return_format, str_replace($searches, $replacements, $return_format)));
  2285. return str_replace($searches, $replacements, $return_format);
  2286. }
  2287. /**
  2288. * Translates a timecode to the number of seconds
  2289. *
  2290. * @access public
  2291. * @param integer $input_seconds The number of seconds you want to calculate the timecode for.
  2292. * @param integer $input_format The format of the timecode is being given in.
  2293. * default '%hh:%mm:%ss'
  2294. * - %hh (hours) representative of hours
  2295. * - %mm (minutes) representative of minutes
  2296. * - %ss (seconds) representative of seconds
  2297. * - %fn (frame number) representative of frames (of the current second, not total frames)
  2298. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  2299. * - %ft (frames total) representative of total frames (ie frame number)
  2300. * - %st (seconds total) representative of total seconds (rounded).
  2301. * - %sf (seconds floored) representative of total seconds (floored).
  2302. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  2303. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  2304. * @param integer $return_format The format of the timecode to return. The default is
  2305. * default '%ts'
  2306. * - %hh (hours) representative of hours
  2307. * - %mm (minutes) representative of minutes
  2308. * - %ss (seconds) representative of seconds
  2309. * - %fn (frame number) representative of frames (of the current second, not total frames)
  2310. * - %ms (milliseconds) representative of milliseconds (of the current second, not total milliseconds) (rounded to 3 decimal places)
  2311. * - %ft (frames total) representative of total frames (ie frame number)
  2312. * - %st (seconds total) representative of total seconds (rounded).
  2313. * - %sf (seconds floored) representative of total seconds (floored).
  2314. * - %sc (seconds ceiled) representative of total seconds (ceiled).
  2315. * - %mt (milliseconds total) representative of total milliseconds. (rounded to 3 decimal places)
  2316. * Thus you could use an alternative, '%hh:%mm:%ss:%ms', or '%hh:%mm:%ss' dependent on your usage.
  2317. * @param mixed|boolean|integer $frames_per_second The number of frames per second to translate for. If left false
  2318. * the class automagically gets the fps from PHPVideoToolkit::getFileInfo(), but the input has to be set
  2319. * first for this to work properly.
  2320. * @param boolean $use_smart_values Default value is true, if a format is found (ie %ss - secs) but no higher format (ie %mm - mins)
  2321. * is found then if $use_smart_values is true the value of of the format will be totaled.
  2322. * @return float Returns the value of the timecode in seconds.
  2323. */
  2324. function formatTimecode($input_timecode, $input_format='%hh:%mm:%ss', $return_format='%ts', $frames_per_second=false, $use_smart_values=true)
  2325. {
  2326. // first we must get the timecode into the current seconds
  2327. $input_quoted = preg_quote($input_format);
  2328. $placeholders = array('%hh', '%mm', '%ss', '%fn', '%ms', '%ft', '%st', '%sf', '%sc', '%mt');
  2329. $seconds = 0;
  2330. $input_regex = str_replace($placeholders, '([0-9]+)', preg_quote($input_format));
  2331. preg_match('/'.$input_regex.'/', $input_timecode, $matches);
  2332. // work out the sort order for the placeholders
  2333. $sort_table = array();
  2334. foreach($placeholders as $key=>$placeholder)
  2335. {
  2336. if(($pos = strpos($input_format, $placeholder)) !== false)
  2337. {
  2338. $sort_table[$pos] = $placeholder;
  2339. }
  2340. }
  2341. ksort($sort_table);
  2342. // check to see if frame related values are in the input
  2343. $has_frames = strpos($input_format, '%fn') !== false;
  2344. $has_total_frames = strpos($input_format, '%ft') !== false;
  2345. if($has_frames || $has_total_frames)
  2346. {
  2347. // if the fps is false then we must automagically detect it from the input file
  2348. if($frames_per_second === false)
  2349. {
  2350. $info = $this->getFileInfo();
  2351. // check the information has been received
  2352. if($info === false || (!isset($info['video']) || !isset($info['video']['frame_rate'])))
  2353. {
  2354. // fps cannot be reached so return -1
  2355. return -1;
  2356. }
  2357. $frames_per_second = $info['video']['frame_rate'];
  2358. }
  2359. }
  2360. // increment the seconds with each placeholder value
  2361. $key = 1;
  2362. foreach($sort_table as $placeholder)
  2363. {
  2364. if(!isset($matches[$key]))
  2365. {
  2366. break;
  2367. }
  2368. $value = $matches[$key];
  2369. switch($placeholder)
  2370. {
  2371. // time related ones
  2372. case '%hh' :
  2373. $seconds += $value * 3600;
  2374. break;
  2375. case '%mm' :
  2376. $seconds += $value * 60;
  2377. break;
  2378. case '%ss' :
  2379. case '%sf' :
  2380. case '%sc' :
  2381. $seconds += $value;
  2382. break;
  2383. case '%ms' :
  2384. $seconds += floatval('0.'.$value);
  2385. break;
  2386. case '%st' :
  2387. case '%mt' :
  2388. $seconds = $value;
  2389. break 1;
  2390. break;
  2391. // frame related ones
  2392. case '%fn' :
  2393. $seconds += $value/$frames_per_second;
  2394. break;
  2395. case '%ft' :
  2396. $seconds = $value/$frames_per_second;
  2397. break 1;
  2398. break;
  2399. }
  2400. $key += 1;
  2401. }
  2402. // then we just format the seconds
  2403. return $this->formatSeconds($seconds, $return_format, $frames_per_second, $use_smart_values);
  2404. }
  2405. /**
  2406. * This is a function that joins multiple input sources into one source before
  2407. * the final processing takes place. All videos are temporarily converted into mpg for
  2408. * joining.
  2409. *
  2410. * PLEASE NOTE. This process is experimental an might not work on all systems.
  2411. *
  2412. * @access private
  2413. * @param boolean $log
  2414. */
  2415. function _joinInput($log)
  2416. {
  2417. die('INPUT CANNOT YET BE JOINED.');
  2418. // ---- ffmpeg works
  2419. /*
  2420. mkfifo /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate1.mpg
  2421. mkfifo /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate2.mpg
  2422. ffmpeg -i /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/MOV02820.MPG -sameq -y /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate1.mpg < /dev/null &
  2423. ffmpeg -i /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/MOV02832.MPG -sameq -y /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate2.mpg < /dev/null &
  2424. cat /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate1.mpg /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/intermediate2.mpg |\
  2425. ffmpeg -f mpeg -i - -sameq -vcodec flv -acodec mp3 -ar 22050 /Users/ollie/Sites/@Projects/ffmpeg/checkout/root/examples/tmp/output.flv
  2426. */
  2427. // ---- mencoder works
  2428. /*
  2429. PHPVIDEOTOOLKIT_MENCODER_BINARY.' -oac copy -ovc copy -idx -o '.$temp_file.' '.implode(' ', $this->_input_file);
  2430. */
  2431. // run a libmp3lame check as it require different mp3 codec
  2432. $audio_codec = 'mp3';
  2433. $info = $this->getFFmpegInfo();
  2434. if(isset($info['binary']['configuration']) && in_array('--enable-libmp3lame', $info['binary']['configuration']))
  2435. {
  2436. $audio_codec = 'libmp3lame';
  2437. }
  2438. // build commands
  2439. $temp_files = array();
  2440. $mkinfo_commands = array();
  2441. $ffmpeg_commands = array();
  2442. $cat_files = array();
  2443. $unique = $this->unique();
  2444. foreach($this->_input_file as $key=>$file)
  2445. {
  2446. $unique_name = $this->_tmp_directory.$unique.'-'.$key.'-temp.mpg';
  2447. $unique_name_escaped = escapeshellarg($unique_name);
  2448. $logfile1 = $this->_tmp_directory.$unique.'-'.$key.'-log1.log';
  2449. $logfile2 = $this->_tmp_directory.$unique.'-'.$key.'-log2.log';
  2450. array_push($mkinfo_commands, array('cmd'=> 'mkfifo '.$unique_name_escaped.($log ? ' &> '.$logfile1 : ''), 'logfile'=>$logfile1));
  2451. array_push($ffmpeg_commands, array('cmd'=> PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.escapeshellarg($file).' -acodec '.$audio_codec.' -sameq '.$unique_name_escaped.' < /dev/null '.($log ? '&> '.$logfile2 : '&'), 'logfile'=>$logfile2));
  2452. array_push($cat_files, $unique_name_escaped);
  2453. // array_push($this->_unlink_files, $unique_name);
  2454. if($log)
  2455. {
  2456. // array_push($this->_unlink_files, $logfile1);
  2457. // array_push($this->_unlink_files, $logfile2);
  2458. }
  2459. }
  2460. // start log
  2461. if($log)
  2462. {
  2463. $log_lines = array();
  2464. array_unshift($log_lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_join_gunk'), $this->_getMessage('ffmpeg_log_separator'));
  2465. }
  2466. // mkinfo for temp files
  2467. foreach($mkinfo_commands as $cmd)
  2468. {
  2469. // exec($cmd['cmd']);
  2470. echo($cmd['cmd']."\r\n");
  2471. if($log)
  2472. {
  2473. array_push($log_lines, '---------', trim(file_get_contents($cmd['logfile'])));
  2474. }
  2475. }
  2476. // extract data
  2477. foreach($ffmpeg_commands as $cmd)
  2478. {
  2479. // exec($cmd['cmd']);
  2480. echo($cmd['cmd']."\r\n");
  2481. if($log)
  2482. {
  2483. array_push($log_lines, trim(file_get_contents($cmd['logfile'])), '---------');
  2484. }
  2485. }
  2486. // join command
  2487. $unique = $this->unique();
  2488. $temp_join_file = $this->_tmp_directory.$unique.'-combined-joined.mpg';
  2489. $temp_join_file_escaped = escapeshellarg($temp_join_file);
  2490. $temp_process_file = $this->_tmp_directory.$unique.'-combined-temp.mpg';
  2491. $temp_process_file_escaped = escapeshellarg($temp_process_file);
  2492. $logfile = $this->_tmp_directory.$unique.'.log';
  2493. // command for use with cat mkinfo files
  2494. // exec('cat '.implode(' ', $cat_files).' |\
  2495. // '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -f mpeg -i - -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.escapeshellarg($temp_process_file).($log ? ' &> '.$logfile : ''));
  2496. echo('cat '.implode(' ', $cat_files).' |\
  2497. '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -f mpeg -i - -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.escapeshellarg($temp_process_file).($log ? ' &> '.$logfile : '')."\r\n");
  2498. // echo('cat '.implode(' ', $cat_files).' > '.$temp_join_file_escaped.'
  2499. // '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$temp_join_file_escaped.' -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.$temp_process_file_escaped.($log ? ' &> '.$logfile : ''));
  2500. // exec('cat '.implode(' ', $cat_files).' > '.$temp_join_file_escaped.'
  2501. // '.PHPVIDEOTOOLKIT_FFMPEG_BINARY.' -i '.$temp_join_file_escaped.' -sameq -vcodec mpeg4 -acodec '.$audio_codec.' '.$temp_process_file_escaped.($log ? ' &> '.$logfile : ''));
  2502. if($log)
  2503. {
  2504. array_push($log_lines, trim(file_get_contents($logfile)));
  2505. array_push($this->_unlink_files, $logfile);
  2506. $this->_addToLog($log_lines, 'r+');
  2507. print_r($log_lines);
  2508. }
  2509. // create a temp dir in the temp dir
  2510. // $temp_file = $this->_tmp_directory.$this->unique().'.'.array_pop(explode('.', $this->_process_address));
  2511. // print_r($temp_file);
  2512. $this->addCommand('-i', $temp_process_file);
  2513. // array_push($this->_unlink_files, $temp_process_file);
  2514. exit;
  2515. }
  2516. /**
  2517. * Checks to see if a given codec can be encoded by the current ffmpeg binary.
  2518. * @access public
  2519. * @param $codec string The shortcode for the codec to check for.
  2520. * @return boolean True if the codec can be encoded by ffmpeg, otherwise false.
  2521. */
  2522. function canCodecBeEncoded($codec)
  2523. {
  2524. return $this->validateCodec($codec, 'encode');
  2525. }
  2526. /**
  2527. * Checks to see if a given codec can be decoded by the current ffmpeg binary.
  2528. * @access public
  2529. * @param $codec string The shortcode for the codec to check for.
  2530. * @return boolean True if the codec can be decoded by ffmpeg, otherwise false.
  2531. */
  2532. function canCodecBeDecoded($codec)
  2533. {
  2534. return $this->validateCodec($codec, 'decode');
  2535. }
  2536. /**
  2537. * Checks to see if a given codec can be decoded by the current ffmpeg binary.
  2538. * @access public
  2539. * @param $codec string The shortcode for the codec to check for.
  2540. * @param $method string 'encode' or 'decode', The method to check against the codec
  2541. * @return boolean True if the codec can be used with the diven method by ffmpeg, otherwise false.
  2542. */
  2543. function validateCodec($codec, $method)
  2544. {
  2545. $info = $this->getFFmpegInfo();
  2546. return isset($info['formats'][$codec]) && isset($info['formats'][$codec][$method]) ? $info['formats'][$codec][$method] : false;
  2547. }
  2548. /**
  2549. * Returns the available codecs.
  2550. * @access public
  2551. * @return array An array of codecs available to ffmpeg.
  2552. */
  2553. function getAvailableCodecs()
  2554. {
  2555. $info = $this->getFFmpegInfo();
  2556. return array_keys($info['formats']);
  2557. }
  2558. /**
  2559. * Commits all the commands and executes the ffmpeg procedure. This will also attempt to validate any outputted files in order to provide
  2560. * some level of stop and check system.
  2561. *
  2562. * @access public
  2563. * @param $multi_pass_encode boolean Determines if multi (2) pass encoding should be used.
  2564. * @param $log boolean Determines if a log file of the results should be generated.
  2565. * @return mixed
  2566. * - false On error encountered.
  2567. * - PHPVIDEOTOOLKIT_RESULT_OK (bool true) If the file has successfully been processed and moved ok to the output address
  2568. * - 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
  2569. * If this is the case you will manually need to move the processed file from the temp directory. You can
  2570. * get around this by settings the third argument from PHPVideoToolkit::setOutput(), $overwrite to true.
  2571. * - n (int) A positive integer is only returned when outputting a series of frame grabs from a movie. It dictates
  2572. * the total number of frames grabbed from the input video. You should also not however, that if a conflict exists
  2573. * with one of the filenames then this return value will not be returned, but PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE
  2574. * will be returned instead.
  2575. * Because of the mixed return value you should always go a strict evaluation of the returned value. ie
  2576. *
  2577. * $result = $toolkit->excecute();
  2578. * if($result === false)
  2579. * {
  2580. * // error
  2581. * }
  2582. * else if($result === PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE)
  2583. * {
  2584. * // ok but a manual move is required. The file to move can be it can be retrieved by $toolkit->getLastOutput();
  2585. * }
  2586. * else if($result === PHPVIDEOTOOLKIT_RESULT_OK)
  2587. * {
  2588. * // everything is ok.
  2589. * }
  2590. */
  2591. function execute($multi_pass_encode=false, $log=false)
  2592. {
  2593. // check for inut and output params
  2594. $has_placeholder = preg_match('/\%([0-9]+)index/', $this->_process_address) || (strpos($this->_process_address, '%index') === false && strpos($this->_process_address, '%timecode') === false);
  2595. if($this->_input_file === null && !$has_placeholder)
  2596. {
  2597. return $this->_raiseError('execute_input_404');
  2598. //<- exits
  2599. }
  2600. // check to see if the output address has been set
  2601. if($this->_process_address === null)
  2602. {
  2603. return $this->_raiseError('execute_output_not_set');
  2604. //<- exits
  2605. }
  2606. // check if temp dir is required and is writable
  2607. if(($multi_pass_encode || $log) && !is_writable($this->_tmp_directory))
  2608. {
  2609. return $this->_raiseError('execute_temp_unwritable');
  2610. //<- exits
  2611. }
  2612. if(($this->_overwrite_mode == PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE || $this->_overwrite_mode == PHPVIDEOTOOLKIT_OVERWRITE_FAIL) && is_file($this->_process_address))
  2613. {
  2614. return $this->_raiseError('execute_overwrite_process');
  2615. //<- exits
  2616. }
  2617. // carry out some overwrite checks if required
  2618. $overwrite = '';
  2619. switch($this->_overwrite_mode)
  2620. {
  2621. case PHPVIDEOTOOLKIT_OVERWRITE_UNIQUE :
  2622. // insert a unique id into the output address (the process address already has one)
  2623. $unique = $this->unique();
  2624. $last_index = strrpos($this->_output_address, DS);
  2625. $this->_output_address = substr($this->_output_address, 0, $last_index+1).$unique.'-'.substr($this->_output_address, $last_index+1);
  2626. break;
  2627. case PHPVIDEOTOOLKIT_OVERWRITE_EXISTING :
  2628. // add an overwrite command to ffmpeg execution call
  2629. $overwrite = '-y ';
  2630. break;
  2631. case PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE :
  2632. // do nothing as the preservation comes later
  2633. break;
  2634. case PHPVIDEOTOOLKIT_OVERWRITE_FAIL :
  2635. default :
  2636. // if the file should fail
  2637. if(!$has_placeholder && is_file($this->_output_address))
  2638. {
  2639. return $this->_raiseError('execute_overwrite_fail');
  2640. //<- exits
  2641. }
  2642. break;
  2643. }
  2644. $this->_timer_start = PHPVideoToolkit::microtimeFloat();
  2645. // we have multiple inputs that require joining so convert them to a joinable format and join
  2646. if(is_array($this->_input_file))
  2647. {
  2648. $this->_joinInput($log);
  2649. }
  2650. // add the input file command to the mix
  2651. $this->addCommand('-i', $this->_input_file);
  2652. // if multi pass encoding is enabled add the commands and logfile
  2653. if($multi_pass_encode)
  2654. {
  2655. $multi_pass_file = $this->_tmp_directory.$this->unique().'-multipass';
  2656. $this->addCommand('-pass', 1);
  2657. $this->addCommand('-passlogfile', $multi_pass_file);
  2658. }
  2659. // check to see if the format has been set and if it hasn't been set and the extension is a gif
  2660. // we need to add an extra argument to set the pix format.
  2661. $format = $this->hasCommand('-f');
  2662. if($format === false)
  2663. {
  2664. $extension = strtolower(array_pop(explode('.', $this->_input_file)));
  2665. if($extension === 'gif')
  2666. {
  2667. $this->addCommand('-pix_fmt', 'rgb24');
  2668. }
  2669. }
  2670. else if($format === PHPVIDEOTOOLKIT_FORMAT_GIF)
  2671. {
  2672. $this->addCommand('-pix_fmt', 'rgb24');
  2673. }
  2674. // check to see if an aspect ratio is set, if it is correct the width and heights to reflect that aspect ratio.
  2675. // This isn't strictly needed it is purely for informational purposes that this is done, because if the width is not
  2676. // inline with what is should be according to the aspect ratio ffmpeg will report the wrong final width and height
  2677. // when using it to lookup information about the file.
  2678. $ratio = $this->hasCommand('-aspect');
  2679. if($ratio !== false)
  2680. {
  2681. $size = $this->hasCommand('-s');
  2682. if($size === false)
  2683. {
  2684. $info = $this->getFileInfo();
  2685. if(isset($info['video']) && isset($info['video']['dimensions']))
  2686. {
  2687. $size = $info['video']['dimensions']['width'].'x'.$info['video']['dimensions']['height'];
  2688. }
  2689. }
  2690. if($size !== false)
  2691. {
  2692. $dim = explode('x', substr($size, 1, -1));
  2693. if(($boundry = strpos($ratio, ':')) !== false)
  2694. {
  2695. $ratio = substr($ratio, 1, $boundry-1)/substr($ratio, $boundry+1, -1);
  2696. $new_width = round($dim[1]*$ratio);
  2697. // make sure new width is an even number
  2698. $ceiled = ceil($new_width);
  2699. $new_width = $ceiled % 2 !== 0 ? floor($new_width) : $ceiled;
  2700. if($new_width != $dim[0])
  2701. {
  2702. $this->setVideoDimensions($new_width, $dim[1]);
  2703. }
  2704. }
  2705. else if(strpos($ratio, '.') !== false)
  2706. {
  2707. $ratio = floatval($ratio);
  2708. $new_width = $dim[1]*$ratio;
  2709. // make sure new width is an even number
  2710. $ceiled = ceil($new_width);
  2711. $new_width = $ceiled % 2 !== 0 ? floor($new_width) : $ceiled;
  2712. if($new_width != $dim[0])
  2713. {
  2714. $this->setVideoDimensions($new_width, $dim[1]);
  2715. }
  2716. }
  2717. }
  2718. }
  2719. // combine all the output commands
  2720. $command_string = $this->_combineCommands();
  2721. // prepare the command suitable for exec
  2722. // the input and overwrite commands have specific places to be set so they have to be added outside of the combineCommands function
  2723. $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FFMPEG_BINARY, $command_string, $overwrite.escapeshellcmd($this->_process_address));
  2724. // $exec_string = $this->_prepareCommand(PHPVIDEOTOOLKIT_FFMPEG_BINARY, '-i '.$this->_commands['-i'].' '.$command_string, $overwrite.escapeshellcmd($this->_process_address));
  2725. if($log)
  2726. {
  2727. $this->_log_file = $this->_tmp_directory.$this->unique().'.info';
  2728. array_push($this->_unlink_files, $this->_log_file);
  2729. $exec_string = $exec_string.' &> '.$this->_log_file;
  2730. }
  2731. // execute the command
  2732. exec($exec_string);
  2733. // track the processed command by adding it to the class
  2734. array_unshift($this->_processed, $exec_string);
  2735. // create the multiple pass encode
  2736. if($multi_pass_encode)
  2737. {
  2738. $pass2_exc_string = str_replace('-pass '.escapeshellarg(1), '-pass '.escapeshellarg(2), $exec_string);
  2739. exec($pass2_exc_string);
  2740. $this->_processed[0] = array($this->_processed[0], $pass2_exc_string);
  2741. // remove the multipass log file
  2742. unlink($multi_pass_file.'-0.log');
  2743. }
  2744. // keep track of the time taken
  2745. $execution_time = PHPVideoToolkit::microtimeFloat() - $this->_timer_start;
  2746. array_unshift($this->_timers, $execution_time);
  2747. // add the exec string to the log file
  2748. if($log)
  2749. {
  2750. $lines = $this->_processed[0];
  2751. if(!is_array($lines))
  2752. {
  2753. $lines = array($lines);
  2754. }
  2755. array_unshift($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_command'), $this->_getMessage('ffmpeg_log_separator'));
  2756. array_unshift($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_gunk'), $this->_getMessage('ffmpeg_log_separator'));
  2757. $this->_addToLog($lines, 'r+');
  2758. }
  2759. // exit
  2760. // must validate a series of outputed items
  2761. // detect if the output address is a sequence output
  2762. if(preg_match('/\%([0-9]+)d/', $this->_process_address, $d_matches) || strpos($this->_process_address, '%d') !== false)
  2763. {
  2764. // get the path details
  2765. $process_info = pathinfo($this->_process_address);
  2766. $output_info = pathinfo($this->_output_address);
  2767. $pad_amount = intval($d_matches[1]);
  2768. // print_r(array($process_info, $output_info));
  2769. // get the %index padd amounts
  2770. $has_preg_index = preg_match('/\%([0-9]+)index/', $output_info['basename'], $index_matches);
  2771. $output_index_pad_amount = isset($index_matches[1]) ? intval($index_matches[1], 1) : 0;
  2772. // var_dump($index_matches);
  2773. // init the iteration values
  2774. $num = 1;
  2775. $files = array();
  2776. $produced = array();
  2777. $error = false;
  2778. $name_conflict = false;
  2779. $file_exists = false;
  2780. // get the first files name
  2781. $filename = $process_info['dirname'].DS.str_replace($d_matches[0], str_pad($num, $pad_amount, '0', STR_PAD_LEFT), $process_info['basename']);
  2782. $use_timecode = strpos($output_info['basename'], '%timecode') !== false;
  2783. $use_index = $has_preg_index || strpos($output_info['basename'], '%index') !== false;
  2784. // if(!$use_timecode && $use_index)
  2785. // {
  2786. // if($log)
  2787. // {
  2788. // $this->_logResult('execute_overwrite_fail');
  2789. // }
  2790. // return $this->_raiseError('execute_overwrite_fail');
  2791. // }
  2792. // start the timecode pattern replacement values
  2793. if($use_timecode)
  2794. {
  2795. $secs_start = $this->formatTimecode($this->_image_output_timecode_start, '%hh:%mm:%ss.%ms', '%mt', $this->_image_output_timecode_fps);
  2796. $fps_inc = 1/$this->_image_output_timecode_fps;
  2797. $fps_current_sec = 0;
  2798. $fps_current_frame = 0;
  2799. }
  2800. // loop checking for file existence
  2801. while(@is_file($filename))
  2802. {
  2803. // check for empty file
  2804. $size = filesize($filename);
  2805. if($size == 0)
  2806. {
  2807. $error = true;
  2808. }
  2809. array_push($produced, $filename);
  2810. // create the substitution arrays
  2811. $searches = array();
  2812. $replacements = array();
  2813. if($use_index)
  2814. {
  2815. array_push($searches, isset($index_matches[0]) ? $index_matches[0] : '%index');
  2816. array_push($replacements, str_pad($num, $output_index_pad_amount, '0', STR_PAD_LEFT));
  2817. }
  2818. // check if timecode is in the output name, no need to use it if not
  2819. if($use_timecode)
  2820. {
  2821. $fps_current_sec += $fps_inc;
  2822. $fps_current_frame += 1;
  2823. if($fps_current_sec >= 1)
  2824. {
  2825. $fps_current_sec = $fps_inc;
  2826. $secs_start += 1;
  2827. $fps_current_frame = 1;
  2828. }
  2829. $timecode = $this->formatSeconds($secs_start, $this->image_output_timecode_format, $this->_image_output_timecode_fps);
  2830. $timecode = str_replace(array(':', '.'), $this->timecode_seperator_output, $timecode);
  2831. // add to the substitution array
  2832. array_push($searches, '%timecode');
  2833. array_push($replacements, $timecode);
  2834. }
  2835. // check if the file exists already and if it does check that it can be overriden
  2836. $old_filename = $filename;
  2837. // print_r(array($searches, $replacements, $output_info['basename']));
  2838. $new_file = str_replace($searches, $replacements, $output_info['basename']);
  2839. $new_filename = $output_info['dirname'].DS.$new_file;
  2840. // var_dump($filename, $new_filename);
  2841. if(!is_file($new_filename) || $this->_overwrite_mode == PHPVIDEOTOOLKIT_OVERWRITE_EXISTING)
  2842. {
  2843. rename($filename, $new_filename);
  2844. $filename = $new_filename;
  2845. }
  2846. // the file exists and is not allowed to be overriden so just rename in the temp directory using the timecode
  2847. else if($this->_overwrite_mode == PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE)
  2848. {
  2849. $new_filename = $process_info['dirname'].DS.'tbm-'.$this->unique().'-'.$new_file;
  2850. rename($filename, $new_filename);
  2851. $filename = $new_filename;
  2852. // add the error to the log file
  2853. if($log)
  2854. {
  2855. $this->_logResult('execute_image_file_exists', array('file'=>$new_filename));
  2856. }
  2857. // flag the conflict
  2858. $file_exists = true;
  2859. }
  2860. // the file exists so the process must fail
  2861. else
  2862. {
  2863. // add the error to the log file
  2864. if($log)
  2865. {
  2866. $this->_logResult('execute_overwrite_fail');
  2867. }
  2868. // tidy up the produced files
  2869. array_merge($this->_unlink_files, $produced);
  2870. return $this->_raiseError('execute_overwrite_fail');
  2871. }
  2872. // process the name change if the %d is to be replaced with the timecode
  2873. $num += 1;
  2874. $files[$filename] = $size > 0 ? basename($filename) : false;
  2875. // print_r("\r\n\r\n".is_file($old_filename)." - ".$old_filename.' => '.$new_filename);
  2876. // print_r($files);
  2877. // get the next incremented filename to check for existance
  2878. $filename = $process_info['dirname'].DS.str_replace($d_matches[0], str_pad($num, $pad_amount, '0', STR_PAD_LEFT), $process_info['basename']);
  2879. }
  2880. // de-increment the last num as it wasn't found
  2881. $num -= 1;
  2882. // if the file was detected but were empty then display a different error
  2883. if($error === true)
  2884. {
  2885. // add the error to the log file
  2886. if($log)
  2887. {
  2888. $this->_logResult('execute_partial_error', array('input'=>$this->_input_file));
  2889. }
  2890. return $this->_raiseError('execute_partial_error', array('input'=>$this->_input_file));
  2891. //<- exits
  2892. }
  2893. // post process any files
  2894. // print_r($files);
  2895. $post_process_result = $this->_postProcess($log, $files);
  2896. // print_r($files);
  2897. if(is_array($post_process_result))
  2898. {
  2899. // post process has occurred and everything is fine
  2900. $num = count($files);
  2901. }
  2902. else if($post_process_result !== false)
  2903. {
  2904. // the file has encountered an error in the post processing of the files
  2905. return $post_process_result;
  2906. }
  2907. // var_dump("\r\n\r\n", $files, __LINE__, __FILE__, "\r\n\r\n");
  2908. // exit;
  2909. // if the result is false then no post process has taken place
  2910. $this->_process_file_count = $num;
  2911. // no files were generated in this sequence
  2912. if($num == 0)
  2913. {
  2914. // add the error to the log file
  2915. if($log)
  2916. {
  2917. $this->_logResult('execute_image_error', array('input'=>$this->_input_file));
  2918. }
  2919. return $this->_raiseError('execute_image_error', array('input'=>$this->_input_file));
  2920. //<- exits
  2921. }
  2922. // add the files the the class a record of what has been generated
  2923. array_unshift($this->_files, $files);
  2924. array_push($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_output'), $this->_getMessage('ffmpeg_log_separator'), implode("\n", $files));
  2925. $this->_addToLog($lines, 'r+');
  2926. return $file_exists ? PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE : PHPVIDEOTOOLKIT_RESULT_OK;
  2927. }
  2928. // must validate one file
  2929. else
  2930. {
  2931. // check that it is a file
  2932. if(!is_file($this->_process_address))
  2933. {
  2934. // add the error to the log file
  2935. if($log)
  2936. {
  2937. $this->_logResult('execute_output_404', array('input'=>$this->_input_file));
  2938. }
  2939. return $this->_raiseError('execute_output_404', array('input'=>$this->_input_file));
  2940. //<- exits
  2941. }
  2942. // the file does exist but is it empty?
  2943. if(filesize($this->_process_address) == 0)
  2944. {
  2945. // add the error to the log file
  2946. if($log)
  2947. {
  2948. $this->_logResult('execute_output_empty', array('input'=>$this->_input_file));
  2949. }
  2950. return $this->_raiseError('execute_output_empty', array('input'=>$this->_input_file));
  2951. //<- exits
  2952. }
  2953. // the file is ok so move to output address
  2954. if(!is_file($this->_output_address) || $this->_overwrite_mode == PHPVIDEOTOOLKIT_OVERWRITE_EXISTING)
  2955. {
  2956. // post process any files
  2957. $post_process_result = $this->_postProcess($log, array($this->_process_address));
  2958. if(is_array($post_process_result) || $post_process_result === true)
  2959. {
  2960. // post process has occurred and everything is fine
  2961. }
  2962. else if($post_process_result !== false)
  2963. {
  2964. return $post_process_result;
  2965. }
  2966. // if the result is false then no post process has taken place
  2967. // rename the file to the final destination and check it went ok
  2968. if(rename($this->_process_address, $this->_output_address))
  2969. {
  2970. array_push($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_output'), $this->_getMessage('ffmpeg_log_separator'), $this->_output_address);
  2971. $this->_addToLog($lines, 'r+');
  2972. // the file has been renamed ok
  2973. // add the error to the log file
  2974. if($log)
  2975. {
  2976. $this->_logResult('execute_result_ok', array('output'=>$this->_output_address));
  2977. }
  2978. $this->_process_file_count = 1;
  2979. // add the file the the class a record of what has been generated
  2980. array_unshift($this->_files, array($this->_output_address));
  2981. return PHPVIDEOTOOLKIT_RESULT_OK;
  2982. }
  2983. // renaming failed so return ok but erro
  2984. else
  2985. {
  2986. // add the error to the log file
  2987. if($log)
  2988. {
  2989. $this->_logResult('execute_result_ok_but_unwritable', array('process'=>$this->_process_address, 'output'=>$this->_output_address));
  2990. }
  2991. // add the file the the class a record of what has been generated
  2992. array_unshift($this->_files, array($this->_process_address));
  2993. array_push($lines, $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_output'), $this->_getMessage('ffmpeg_log_separator'), $this->_process_address);
  2994. $this->_addToLog($lines, 'r+');
  2995. return PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE;
  2996. }
  2997. }
  2998. // if it is not we signal that it has been created but has not been moved.
  2999. else if($this->_overwrite_mode == PHPVIDEOTOOLKIT_OVERWRITE_PRESERVE)
  3000. {
  3001. // add the error to the log file
  3002. if($log)
  3003. {
  3004. $this->_logResult('execute_result_ok_but_unwritable', array('process'=>$this->_process_address, 'output'=>$this->_output_address));
  3005. }
  3006. // add the file the the class a record of what has been generated
  3007. array_unshift($this->_files, array($this->_process_address));
  3008. return PHPVIDEOTOOLKIT_RESULT_OK_BUT_UNWRITABLE;
  3009. }
  3010. // the file exists so the process must fail
  3011. else
  3012. {
  3013. // add the error to the log file
  3014. if($log)
  3015. {
  3016. $this->_logResult('execute_overwrite_fail');
  3017. }
  3018. // tidy up the produced files
  3019. array_push($this->_unlink_files, $this->_process_address);
  3020. return $this->_raiseError('execute_overwrite_fail');
  3021. }
  3022. }
  3023. return null;
  3024. }
  3025. /**
  3026. * This function registers a post process after the internal handling of the ffmpeg output has been cleaned and checked.
  3027. * Each function that is set will be called in the order it is set unless an index is specified. All callbacks will be
  3028. * supplied with one argument with is an array of the outputted files.
  3029. *
  3030. * NOTE1: If a post process function is being applied to an outputted video or audio then the process will be applied
  3031. * before it has been moved to it's final destination, however if the output is an image sequence the post process
  3032. * function will be called after the images have been moved to their final destinations.
  3033. *
  3034. * NOTE2: Also it is important to return a boolean 'true' if the post process has been carried out ok. If the process is not
  3035. * a true value then the value will be treated/returned as an error and if applicable logged.
  3036. *
  3037. * @access public
  3038. * @param string $function The name of a function
  3039. * @param object|boolean $class The name of the callback class. If left as false the callback will be treated as a standalone function.
  3040. * @param integer|boolean $index The index of the callback array to put the callback into. If left as false it will be pushed to the end of the array.
  3041. */
  3042. function registerPostProcess($function, $class=false, $index=false)
  3043. {
  3044. // create the callback
  3045. $callback = $class === false ? $function : array(&$class, $function);
  3046. // add it to the post process array
  3047. if($index === false)
  3048. {
  3049. array_push($this->_post_processes, $callback);
  3050. }
  3051. else
  3052. {
  3053. $this->_post_processes[$index] = $callback;
  3054. }
  3055. }
  3056. /**
  3057. * Carries out the post processing of the files.
  3058. *
  3059. * @access private
  3060. * @param boolean $log Determines if logging of errors should be carried out.
  3061. * @param array $files The array of files that have just been processed.
  3062. * @return mixed
  3063. */
  3064. function _postProcess($log, $files)
  3065. {
  3066. if(count($this->_post_processes))
  3067. {
  3068. // loop through the post processes
  3069. foreach($this->_post_processes as $key=>$process)
  3070. {
  3071. // call the process
  3072. $return_value = call_user_func_array($process, array($files));
  3073. // if the return value is not strictly equal to true the result will be treated as an error and exit the process loop
  3074. if(!is_array($return_value) && $return_value !== true)
  3075. {
  3076. if($log)
  3077. {
  3078. $this->_logResult($return_value);
  3079. }
  3080. return $this->_raiseError($return_value);
  3081. }
  3082. }
  3083. return $return_value;
  3084. }
  3085. return false;
  3086. }
  3087. /**
  3088. * Returns the number of files outputted in this run. It will be reset when you call PHPVideoToolkit::reset();
  3089. *
  3090. * @access public
  3091. * @return integer
  3092. */
  3093. function getFileOutputCount()
  3094. {
  3095. return $this->_process_file_count;
  3096. }
  3097. /**
  3098. * Adds lines to the current log file.
  3099. *
  3100. * @access private
  3101. * @param $message
  3102. * @param $replacements
  3103. */
  3104. function _logResult($message, $replacements=false)
  3105. {
  3106. $this->_addToLog(array($this->_getMessage('ffmpeg_log_separator'), $this->_getMessage('ffmpeg_log_ffmpeg_result'), $this->_getMessage('ffmpeg_log_separator'), $this->_getMessage($message, $replacements)));
  3107. }
  3108. /**
  3109. * Adds lines to the current log file.
  3110. *
  3111. * @access private
  3112. * @param $lines array An array of lines to add to the log file.
  3113. */
  3114. function _addToLog($lines, $where='a')
  3115. {
  3116. $handle = fopen($this->_log_file, $where);
  3117. if(is_array($lines))
  3118. {
  3119. $data = implode("\n", $lines)."\n";
  3120. }
  3121. else
  3122. {
  3123. $data = $lines."\n";
  3124. }
  3125. fwrite($handle, $data);
  3126. fclose($handle);
  3127. }
  3128. /**
  3129. * Moves the current log file to another file.
  3130. *
  3131. * @access public
  3132. * @param $destination string The absolute path of the new filename for the log.
  3133. * @return boolean Returns the result of the log file rename.
  3134. */
  3135. function moveLog($destination)
  3136. {
  3137. $result = false;
  3138. if($this->_log_file !== null && is_file($this->_log_file))
  3139. {
  3140. $result = rename($this->_log_file, $destination);
  3141. $this->_log_file = $destination;
  3142. }
  3143. return $result;
  3144. }
  3145. /**
  3146. * Reads the current log file
  3147. *
  3148. * @access public
  3149. * @return string|boolean Returns the current log file content. Returns false on failure.
  3150. */
  3151. function readLog()
  3152. {
  3153. if($this->_log_file !== null && is_file($this->_log_file))
  3154. {
  3155. $handle = fopen($this->_log_file, 'r');
  3156. $contents = fread($handle, filesize($this->_log_file));
  3157. fclose($handle);
  3158. return $contents;
  3159. }
  3160. return false;
  3161. }
  3162. /**
  3163. * Returns the last outputted file that was processed by ffmpeg from this class.
  3164. *
  3165. * @access public
  3166. * @return mixed array|string Will return an array if the output was a sequence, or string if it was a single file output
  3167. */
  3168. function getLastOutput()
  3169. {
  3170. return $this->_files[0];
  3171. }
  3172. /**
  3173. * Returns all the outputted files that were processed by ffmpeg from this class.
  3174. *
  3175. * @access public
  3176. * @return array
  3177. */
  3178. function getOutput()
  3179. {
  3180. return $this->_files;
  3181. }
  3182. /**
  3183. * Returns the amount of time taken of the last file to be processed by ffmpeg.
  3184. *
  3185. * @access public
  3186. * @return mixed integer Will return the time taken in seconds.
  3187. */
  3188. function getLastProcessTime()
  3189. {
  3190. return $this->_timers[0];
  3191. }
  3192. /**
  3193. * Returns the amount of time taken of all the files to be processed by ffmpeg.
  3194. *
  3195. * @access public
  3196. * @return array
  3197. */
  3198. function getProcessTime()
  3199. {
  3200. return $this->_timers;
  3201. }
  3202. /**
  3203. * Returns the last encountered error message.
  3204. *
  3205. * @access public
  3206. * @return string
  3207. */
  3208. function getLastError()
  3209. {
  3210. return $this->_errors[0];
  3211. }
  3212. /**
  3213. * Returns all the encountered errors as an array of strings
  3214. *
  3215. * @access public
  3216. * @return array
  3217. */
  3218. function getErrors()
  3219. {
  3220. return $this->_errors;
  3221. }
  3222. /**
  3223. * Returns the last command that ffmpeg was given.
  3224. * (Note; if setFormatToFLV was used in the last command then an array is returned as a command was also sent to FLVTool2)
  3225. *
  3226. * @access public
  3227. * @return mixed array|string
  3228. */
  3229. function getLastCommand()
  3230. {
  3231. return $this->_processed[0];
  3232. }
  3233. /**
  3234. * Returns all the commands sent to ffmpeg from this class
  3235. *
  3236. * @access public
  3237. * @return unknown
  3238. */
  3239. function getCommands()
  3240. {
  3241. return $this->_processed;
  3242. }
  3243. /**
  3244. * Raises an error
  3245. *
  3246. * @access private
  3247. * @param string $message
  3248. * @param array $replacements a list of replacements in search=>replacement format
  3249. * @return boolean Only returns false if $toolkit->on_error_die is set to false
  3250. */
  3251. function _raiseError($message, $replacements=false)
  3252. {
  3253. $msg = 'PHPVideoToolkit Error: '.$this->_getMessage($message, $replacements);
  3254. // check what the error is supposed to do
  3255. if($this->on_error_die === true)
  3256. {
  3257. die($msg);
  3258. //<- exits
  3259. }
  3260. // add the error message to the collection
  3261. array_unshift($this->_errors, $msg);
  3262. return false;
  3263. }
  3264. /**
  3265. * Gets a message.
  3266. *
  3267. * @access private
  3268. * @param string $message
  3269. * @param array $replacements a list of replacements in search=>replacement format
  3270. * @return boolean Only returns false if $toolkit->on_error_die is set to false
  3271. */
  3272. function _getMessage($message, $replacements=false)
  3273. {
  3274. $message = isset($this->_messages[$message]) ? $this->_messages[$message] : 'Unknown!!!';
  3275. if($replacements)
  3276. {
  3277. $searches = $replaces = array();
  3278. foreach($replacements as $search=>$replace)
  3279. {
  3280. array_push($searches, '#'.$search);
  3281. array_push($replaces, $replace);
  3282. }
  3283. $message = str_replace($searches, $replaces, $message);
  3284. }
  3285. return $message;
  3286. }
  3287. /**
  3288. * Adds a command to be bundled into the ffmpeg command call.
  3289. * (SPECIAL NOTE; None of the arguments are checked or sanitized by this function. BE CAREFUL if manually using this. The commands and arguments are escaped
  3290. * however it is still best to check and sanitize any params given to this function)
  3291. *
  3292. * @access public
  3293. * @param string $command
  3294. * @param mixed $argument
  3295. * @return boolean
  3296. */
  3297. function addCommand($command, $argument=false)
  3298. {
  3299. $this->_commands[$command] = $argument === false ? false : escapeshellarg($argument);
  3300. return true;
  3301. }
  3302. /**
  3303. * Determines if the the command exits.
  3304. *
  3305. * @access public
  3306. * @param string $command
  3307. * @return mixed boolean if failure or value if exists.
  3308. */
  3309. function hasCommand($command)
  3310. {
  3311. return isset($this->_commands[$command]) ? ($this->_commands[$command] === false ? true : $this->_commands[$command]): false;
  3312. }
  3313. /**
  3314. * Combines the commands stored into a string
  3315. *
  3316. * @access private
  3317. * @return string
  3318. */
  3319. function _combineCommands()
  3320. {
  3321. $before_input = array();
  3322. $after_input = array();
  3323. $input = null;
  3324. foreach ($this->_commands as $command=>$argument)
  3325. {
  3326. $command_string = trim($command.(!empty($argument) ? ' '.$argument : ''));
  3327. // check for specific none combinable commands as they have specific places they have to go in the string
  3328. switch($command)
  3329. {
  3330. case '-i' :
  3331. $input = $command_string;
  3332. break;
  3333. case '-inputr' :
  3334. $command_string = trim('-r'.($argument ? ' '.$argument : ''));;
  3335. default :
  3336. if(in_array($command, $this->_cmds_before_input))
  3337. {
  3338. array_push($before_input, $command_string);
  3339. }
  3340. else
  3341. {
  3342. array_push($after_input, $command_string);
  3343. }
  3344. }
  3345. }
  3346. $before_input = count($before_input) ? implode(' ', $before_input).' ' : '';
  3347. $after_input_string = ' ';
  3348. if(count($after_input))
  3349. {
  3350. $input .= ' ';
  3351. $after_input_string = implode(' ', $after_input).' ';
  3352. }
  3353. return $before_input.$input.$after_input_string;
  3354. }
  3355. /**
  3356. * Prepares the command for execution
  3357. *
  3358. * @access private
  3359. * @param string $path Path to the binary
  3360. * @param string $command Command string to execute
  3361. * @param string $args Any additional arguments
  3362. * @return string
  3363. */
  3364. function _prepareCommand($path, $command, $args='')
  3365. {
  3366. if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || !preg_match('/\s/', $path))
  3367. {
  3368. return $path.' '.$command.' '.$args;
  3369. }
  3370. return 'start /D "'.$path.'" /B '.$command.' '.$args;
  3371. }
  3372. /**
  3373. * Generates a unique id. Primarily used in jpeg to movie production
  3374. *
  3375. * @access public
  3376. * @param string $prefix
  3377. * @return string
  3378. */
  3379. function unique($prefix='')
  3380. {
  3381. return uniqid($prefix.time().'-');
  3382. }
  3383. /**
  3384. * Destructs ffmpeg and removes any temp files/dirs
  3385. * @access private
  3386. */
  3387. function __destruct()
  3388. {
  3389. // loop through the temp files to remove first as they have to be removed before the dir can be removed
  3390. if(!empty($this->_unlink_files))
  3391. {
  3392. foreach ($this->_unlink_files as $key=>$file)
  3393. {
  3394. if(is_file($file))
  3395. {
  3396. @unlink($file);
  3397. }
  3398. }
  3399. $this->_unlink_files = array();
  3400. }
  3401. // loop through the dirs to remove
  3402. if(!empty($this->_unlink_dirs))
  3403. {
  3404. foreach ($this->_unlink_dirs as $key=>$dir)
  3405. {
  3406. if(is_dir($dir))
  3407. {
  3408. @rmdir($dir);
  3409. }
  3410. }
  3411. $this->_unlink_dirs = array();
  3412. }
  3413. }
  3414. }